ESPHome  2024.12.2
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 
315  aerr = write_frame_(msg.data(), msg.size());
316  if (aerr != APIError::OK)
317  return aerr;
318 
319  // start handshake
320  aerr = init_handshake_();
321  if (aerr != APIError::OK)
322  return aerr;
323 
325  }
326  if (state_ == State::HANDSHAKE) {
327  int action = noise_handshakestate_get_action(handshake_);
328  if (action == NOISE_ACTION_READ_MESSAGE) {
329  // waiting for handshake msg
330  ParsedFrame frame;
331  aerr = try_read_frame_(&frame);
332  if (aerr == APIError::BAD_INDICATOR) {
333  send_explicit_handshake_reject_("Bad indicator byte");
334  return aerr;
335  }
337  send_explicit_handshake_reject_("Bad handshake packet len");
338  return aerr;
339  }
340  if (aerr != APIError::OK)
341  return aerr;
342 
343  if (frame.msg.empty()) {
344  send_explicit_handshake_reject_("Empty handshake message");
346  } else if (frame.msg[0] != 0x00) {
347  HELPER_LOG("Bad handshake error byte: %u", frame.msg[0]);
348  send_explicit_handshake_reject_("Bad handshake error byte");
350  }
351 
352  NoiseBuffer mbuf;
353  noise_buffer_init(mbuf);
354  noise_buffer_set_input(mbuf, frame.msg.data() + 1, frame.msg.size() - 1);
355  err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
356  if (err != 0) {
358  HELPER_LOG("noise_handshakestate_read_message failed: %s", noise_err_to_str(err).c_str());
359  if (err == NOISE_ERROR_MAC_FAILURE) {
360  send_explicit_handshake_reject_("Handshake MAC failure");
361  } else {
362  send_explicit_handshake_reject_("Handshake error");
363  }
365  }
366 
367  aerr = check_handshake_finished_();
368  if (aerr != APIError::OK)
369  return aerr;
370  } else if (action == NOISE_ACTION_WRITE_MESSAGE) {
371  uint8_t buffer[65];
372  NoiseBuffer mbuf;
373  noise_buffer_init(mbuf);
374  noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
375 
376  err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
377  if (err != 0) {
379  HELPER_LOG("noise_handshakestate_write_message failed: %s", noise_err_to_str(err).c_str());
381  }
382  buffer[0] = 0x00; // success
383 
384  aerr = write_frame_(buffer, mbuf.size + 1);
385  if (aerr != APIError::OK)
386  return aerr;
387  aerr = check_handshake_finished_();
388  if (aerr != APIError::OK)
389  return aerr;
390  } else {
391  // bad state for action
393  HELPER_LOG("Bad action for handshake: %d", action);
395  }
396  }
397  if (state_ == State::CLOSED || state_ == State::FAILED) {
398  return APIError::BAD_STATE;
399  }
400  return APIError::OK;
401 }
403  std::vector<uint8_t> data;
404  data.resize(reason.length() + 1);
405  data[0] = 0x01; // failure
406  for (size_t i = 0; i < reason.length(); i++) {
407  data[i + 1] = (uint8_t) reason[i];
408  }
409  // temporarily remove failed state
410  auto orig_state = state_;
412  write_frame_(data.data(), data.size());
413  state_ = orig_state;
414 }
415 
417  int err;
418  APIError aerr;
419  aerr = state_action_();
420  if (aerr != APIError::OK) {
421  return aerr;
422  }
423 
424  if (state_ != State::DATA) {
425  return APIError::WOULD_BLOCK;
426  }
427 
428  ParsedFrame frame;
429  aerr = try_read_frame_(&frame);
430  if (aerr != APIError::OK)
431  return aerr;
432 
433  NoiseBuffer mbuf;
434  noise_buffer_init(mbuf);
435  noise_buffer_set_inout(mbuf, frame.msg.data(), frame.msg.size(), frame.msg.size());
436  err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
437  if (err != 0) {
439  HELPER_LOG("noise_cipherstate_decrypt failed: %s", noise_err_to_str(err).c_str());
441  }
442 
443  size_t msg_size = mbuf.size;
444  uint8_t *msg_data = frame.msg.data();
445  if (msg_size < 4) {
447  HELPER_LOG("Bad data packet: size %d too short", msg_size);
449  }
450 
451  // uint16_t type;
452  // uint16_t data_len;
453  // uint8_t *data;
454  // uint8_t *padding; zero or more bytes to fill up the rest of the packet
455  uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
456  uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
457  if (data_len > msg_size - 4) {
459  HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
461  }
462 
463  buffer->container = std::move(frame.msg);
464  buffer->data_offset = 4;
465  buffer->data_len = data_len;
466  buffer->type = type;
467  return APIError::OK;
468 }
470 APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
471  int err;
472  APIError aerr;
473  aerr = state_action_();
474  if (aerr != APIError::OK) {
475  return aerr;
476  }
477 
478  if (state_ != State::DATA) {
479  return APIError::WOULD_BLOCK;
480  }
481 
482  size_t padding = 0;
483  size_t msg_len = 4 + payload_len + padding;
484  size_t frame_len = 3 + msg_len + noise_cipherstate_get_mac_length(send_cipher_);
485  auto tmpbuf = std::unique_ptr<uint8_t[]>{new (std::nothrow) uint8_t[frame_len]};
486  if (tmpbuf == nullptr) {
487  HELPER_LOG("Could not allocate for writing packet");
489  }
490 
491  tmpbuf[0] = 0x01; // indicator
492  // tmpbuf[1], tmpbuf[2] to be set later
493  const uint8_t msg_offset = 3;
494  const uint8_t payload_offset = msg_offset + 4;
495  tmpbuf[msg_offset + 0] = (uint8_t) (type >> 8); // type
496  tmpbuf[msg_offset + 1] = (uint8_t) type;
497  tmpbuf[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len
498  tmpbuf[msg_offset + 3] = (uint8_t) payload_len;
499  // copy data
500  std::copy(payload, payload + payload_len, &tmpbuf[payload_offset]);
501  // fill padding with zeros
502  std::fill(&tmpbuf[payload_offset + payload_len], &tmpbuf[frame_len], 0);
503 
504  NoiseBuffer mbuf;
505  noise_buffer_init(mbuf);
506  noise_buffer_set_inout(mbuf, &tmpbuf[msg_offset], msg_len, frame_len - msg_offset);
507  err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
508  if (err != 0) {
510  HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
512  }
513 
514  size_t total_len = 3 + mbuf.size;
515  tmpbuf[1] = (uint8_t) (mbuf.size >> 8);
516  tmpbuf[2] = (uint8_t) mbuf.size;
517 
518  struct iovec iov;
519  iov.iov_base = &tmpbuf[0];
520  iov.iov_len = total_len;
521 
522  // write raw to not have two packets sent if NAGLE disabled
523  return write_raw_(&iov, 1);
524 }
526  // try send from tx_buf
527  while (state_ != State::CLOSED && !tx_buf_.empty()) {
528  ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
529  if (sent == -1) {
530  if (errno == EWOULDBLOCK || errno == EAGAIN)
531  break;
533  HELPER_LOG("Socket write failed with errno %d", errno);
535  } else if (sent == 0) {
536  break;
537  }
538  // TODO: inefficient if multiple packets in txbuf
539  // replace with deque of buffers
540  tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
541  }
542 
543  return APIError::OK;
544 }
550 APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
551  if (iovcnt == 0)
552  return APIError::OK;
553  APIError aerr;
554 
555  size_t total_write_len = 0;
556  for (int i = 0; i < iovcnt; i++) {
557 #ifdef HELPER_LOG_PACKETS
558  ESP_LOGVV(TAG, "Sending raw: %s",
559  format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
560 #endif
561  total_write_len += iov[i].iov_len;
562  }
563 
564  if (!tx_buf_.empty()) {
565  // try to empty tx_buf_ first
566  aerr = try_send_tx_buf_();
567  if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
568  return aerr;
569  }
570 
571  if (!tx_buf_.empty()) {
572  // tx buf not empty, can't write now because then stream would be inconsistent
573  for (int i = 0; i < iovcnt; i++) {
574  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
575  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
576  }
577  return APIError::OK;
578  }
579 
580  ssize_t sent = socket_->writev(iov, iovcnt);
581  if (is_would_block(sent)) {
582  // operation would block, add buffer to tx_buf
583  for (int i = 0; i < iovcnt; i++) {
584  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
585  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
586  }
587  return APIError::OK;
588  } else if (sent == -1) {
589  // an error occurred
591  HELPER_LOG("Socket write failed with errno %d", errno);
593  } else if ((size_t) sent != total_write_len) {
594  // partially sent, add end to tx_buf
595  size_t to_consume = sent;
596  for (int i = 0; i < iovcnt; i++) {
597  if (to_consume >= iov[i].iov_len) {
598  to_consume -= iov[i].iov_len;
599  } else {
600  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
601  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
602  to_consume = 0;
603  }
604  }
605  return APIError::OK;
606  }
607  // fully sent
608  return APIError::OK;
609 }
610 APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
611  uint8_t header[3];
612  header[0] = 0x01; // indicator
613  header[1] = (uint8_t) (len >> 8);
614  header[2] = (uint8_t) len;
615 
616  struct iovec iov[2];
617  iov[0].iov_base = header;
618  iov[0].iov_len = 3;
619  if (len == 0) {
620  return write_raw_(iov, 1);
621  }
622  iov[1].iov_base = const_cast<uint8_t *>(data);
623  iov[1].iov_len = len;
624 
625  return write_raw_(iov, 2);
626 }
627 
633  int err;
634  memset(&nid_, 0, sizeof(nid_));
635  // const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
636  // err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
637  nid_.pattern_id = NOISE_PATTERN_NN;
638  nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
639  nid_.dh_id = NOISE_DH_CURVE25519;
640  nid_.prefix_id = NOISE_PREFIX_STANDARD;
641  nid_.hybrid_id = NOISE_DH_NONE;
642  nid_.hash_id = NOISE_HASH_SHA256;
643  nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
644 
645  err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
646  if (err != 0) {
648  HELPER_LOG("noise_handshakestate_new_by_id failed: %s", noise_err_to_str(err).c_str());
650  }
651 
652  const auto &psk = ctx_->get_psk();
653  err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
654  if (err != 0) {
656  HELPER_LOG("noise_handshakestate_set_pre_shared_key failed: %s", noise_err_to_str(err).c_str());
658  }
659 
660  err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
661  if (err != 0) {
663  HELPER_LOG("noise_handshakestate_set_prologue failed: %s", noise_err_to_str(err).c_str());
665  }
666  // set_prologue copies it into handshakestate, so we can get rid of it now
667  prologue_ = {};
668 
669  err = noise_handshakestate_start(handshake_);
670  if (err != 0) {
672  HELPER_LOG("noise_handshakestate_start failed: %s", noise_err_to_str(err).c_str());
674  }
675  return APIError::OK;
676 }
677 
679  assert(state_ == State::HANDSHAKE);
680 
681  int action = noise_handshakestate_get_action(handshake_);
682  if (action == NOISE_ACTION_READ_MESSAGE || action == NOISE_ACTION_WRITE_MESSAGE)
683  return APIError::OK;
684  if (action != NOISE_ACTION_SPLIT) {
686  HELPER_LOG("Bad action for handshake: %d", action);
688  }
689  int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
690  if (err != 0) {
692  HELPER_LOG("noise_handshakestate_split failed: %s", noise_err_to_str(err).c_str());
694  }
695 
696  HELPER_LOG("Handshake complete!");
697  noise_handshakestate_free(handshake_);
698  handshake_ = nullptr;
700  return APIError::OK;
701 }
702 
704  if (handshake_ != nullptr) {
705  noise_handshakestate_free(handshake_);
706  handshake_ = nullptr;
707  }
708  if (send_cipher_ != nullptr) {
709  noise_cipherstate_free(send_cipher_);
710  send_cipher_ = nullptr;
711  }
712  if (recv_cipher_ != nullptr) {
713  noise_cipherstate_free(recv_cipher_);
714  recv_cipher_ = nullptr;
715  }
716 }
717 
720  int err = socket_->close();
721  if (err == -1)
722  return APIError::CLOSE_FAILED;
723  return APIError::OK;
724 }
726  int err = socket_->shutdown(how);
727  if (err == -1)
729  if (how == SHUT_RDWR) {
731  }
732  return APIError::OK;
733 }
734 extern "C" {
735 // declare how noise generates random bytes (here with a good HWRNG based on the RF system)
736 void noise_rand_bytes(void *output, size_t len) {
737  if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
738  ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!");
739  arch_restart();
740  }
741 }
742 }
743 #endif // USE_API_NOISE
744 
745 #ifdef USE_API_PLAINTEXT
746 
749  if (state_ != State::INITIALIZE || socket_ == nullptr) {
750  HELPER_LOG("Bad state for init %d", (int) state_);
751  return APIError::BAD_STATE;
752  }
753  int err = socket_->setblocking(false);
754  if (err != 0) {
756  HELPER_LOG("Setting nonblocking failed with errno %d", errno);
758  }
759  int enable = 1;
760  err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
761  if (err != 0) {
763  HELPER_LOG("Setting nodelay failed with errno %d", errno);
765  }
766 
768  return APIError::OK;
769 }
772  if (state_ != State::DATA) {
773  return APIError::BAD_STATE;
774  }
775  // try send pending TX data
776  if (!tx_buf_.empty()) {
777  APIError err = try_send_tx_buf_();
778  if (err != APIError::OK) {
779  return err;
780  }
781  }
782  return APIError::OK;
783 }
784 
795  if (frame == nullptr) {
796  HELPER_LOG("Bad argument for try_read_frame_");
797  return APIError::BAD_ARG;
798  }
799 
800  // read header
801  while (!rx_header_parsed_) {
802  uint8_t data;
803  ssize_t received = socket_->read(&data, 1);
804  if (received == -1) {
805  if (errno == EWOULDBLOCK || errno == EAGAIN) {
806  return APIError::WOULD_BLOCK;
807  }
809  HELPER_LOG("Socket read failed with errno %d", errno);
811  } else if (received == 0) {
813  HELPER_LOG("Connection closed");
815  }
816  rx_header_buf_.push_back(data);
817 
818  // try parse header
819  if (rx_header_buf_[0] != 0x00) {
821  HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
823  }
824 
825  size_t i = 1;
826  uint32_t consumed = 0;
827  auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
828  if (!msg_size_varint.has_value()) {
829  // not enough data there yet
830  continue;
831  }
832 
833  i += consumed;
834  rx_header_parsed_len_ = msg_size_varint->as_uint32();
835 
836  auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
837  if (!msg_type_varint.has_value()) {
838  // not enough data there yet
839  continue;
840  }
841  rx_header_parsed_type_ = msg_type_varint->as_uint32();
842  rx_header_parsed_ = true;
843  }
844  // header reading done
845 
846  // reserve space for body
847  if (rx_buf_.size() != rx_header_parsed_len_) {
848  rx_buf_.resize(rx_header_parsed_len_);
849  }
850 
851  if (rx_buf_len_ < rx_header_parsed_len_) {
852  // more data to read
853  size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
854  ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
855  if (received == -1) {
856  if (errno == EWOULDBLOCK || errno == EAGAIN) {
857  return APIError::WOULD_BLOCK;
858  }
860  HELPER_LOG("Socket read failed with errno %d", errno);
862  } else if (received == 0) {
864  HELPER_LOG("Connection closed");
866  }
867  rx_buf_len_ += received;
868  if ((size_t) received != to_read) {
869  // not all read
870  return APIError::WOULD_BLOCK;
871  }
872  }
873 
874  // uncomment for even more debugging
875 #ifdef HELPER_LOG_PACKETS
876  ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
877 #endif
878  frame->msg = std::move(rx_buf_);
879  // consume msg
880  rx_buf_ = {};
881  rx_buf_len_ = 0;
882  rx_header_buf_.clear();
883  rx_header_parsed_ = false;
884  return APIError::OK;
885 }
886 
888  APIError aerr;
889 
890  if (state_ != State::DATA) {
891  return APIError::WOULD_BLOCK;
892  }
893 
894  ParsedFrame frame;
895  aerr = try_read_frame_(&frame);
896  if (aerr != APIError::OK)
897  return aerr;
898 
899  buffer->container = std::move(frame.msg);
900  buffer->data_offset = 0;
901  buffer->data_len = rx_header_parsed_len_;
902  buffer->type = rx_header_parsed_type_;
903  return APIError::OK;
904 }
906 APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
907  if (state_ != State::DATA) {
908  return APIError::BAD_STATE;
909  }
910 
911  std::vector<uint8_t> header;
912  header.push_back(0x00);
913  ProtoVarInt(payload_len).encode(header);
914  ProtoVarInt(type).encode(header);
915 
916  struct iovec iov[2];
917  iov[0].iov_base = &header[0];
918  iov[0].iov_len = header.size();
919  if (payload_len == 0) {
920  return write_raw_(iov, 1);
921  }
922  iov[1].iov_base = const_cast<uint8_t *>(payload);
923  iov[1].iov_len = payload_len;
924 
925  return write_raw_(iov, 2);
926 }
928  // try send from tx_buf
929  while (state_ != State::CLOSED && !tx_buf_.empty()) {
930  ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
931  if (is_would_block(sent)) {
932  break;
933  } else if (sent == -1) {
935  HELPER_LOG("Socket write failed with errno %d", errno);
937  }
938  // TODO: inefficient if multiple packets in txbuf
939  // replace with deque of buffers
940  tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
941  }
942 
943  return APIError::OK;
944 }
950 APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
951  if (iovcnt == 0)
952  return APIError::OK;
953  APIError aerr;
954 
955  size_t total_write_len = 0;
956  for (int i = 0; i < iovcnt; i++) {
957 #ifdef HELPER_LOG_PACKETS
958  ESP_LOGVV(TAG, "Sending raw: %s",
959  format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
960 #endif
961  total_write_len += iov[i].iov_len;
962  }
963 
964  if (!tx_buf_.empty()) {
965  // try to empty tx_buf_ first
966  aerr = try_send_tx_buf_();
967  if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
968  return aerr;
969  }
970 
971  if (!tx_buf_.empty()) {
972  // tx buf not empty, can't write now because then stream would be inconsistent
973  for (int i = 0; i < iovcnt; i++) {
974  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
975  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
976  }
977  return APIError::OK;
978  }
979 
980  ssize_t sent = socket_->writev(iov, iovcnt);
981  if (is_would_block(sent)) {
982  // operation would block, add buffer to tx_buf
983  for (int i = 0; i < iovcnt; i++) {
984  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
985  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
986  }
987  return APIError::OK;
988  } else if (sent == -1) {
989  // an error occurred
991  HELPER_LOG("Socket write failed with errno %d", errno);
993  } else if ((size_t) sent != total_write_len) {
994  // partially sent, add end to tx_buf
995  size_t to_consume = sent;
996  for (int i = 0; i < iovcnt; i++) {
997  if (to_consume >= iov[i].iov_len) {
998  to_consume -= iov[i].iov_len;
999  } else {
1000  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
1001  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
1002  to_consume = 0;
1003  }
1004  }
1005  return APIError::OK;
1006  }
1007  // fully sent
1008  return APIError::OK;
1009 }
1010 
1013  int err = socket_->close();
1014  if (err == -1)
1015  return APIError::CLOSE_FAILED;
1016  return APIError::OK;
1017 }
1019  int err = socket_->shutdown(how);
1020  if (err == -1)
1022  if (how == SHUT_RDWR) {
1024  }
1025  return APIError::OK;
1026 }
1027 #endif // USE_API_PLAINTEXT
1028 
1029 } // namespace api
1030 } // namespace esphome
1031 #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:369
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:217
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
APIError loop() override
Not used for plaintext.
APIError try_read_frame_(ParsedFrame *frame)
Read a packet into the rx_buf_.
uint8_t type
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)
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:81
APIError state_action_()
To be called from read/write methods.
std::string size_t len
Definition: helpers.h:293
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