18#ifdef USE_LWIP_FAST_SELECT
28static const char *
const TAG =
"esphome.ota";
29static constexpr uint16_t OTA_BLOCK_SIZE = 8192;
30static constexpr size_t OTA_BUFFER_SIZE = 1024;
31static constexpr uint32_t OTA_SOCKET_TIMEOUT_HANDSHAKE = 20000;
32static constexpr uint32_t OTA_SOCKET_TIMEOUT_DATA = 90000;
36static ESPHomeOTAComponent *global_esphome_ota_component =
nullptr;
40 if (global_esphome_ota_component !=
nullptr) {
52 int err = this->
server_->
setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(
int));
84 global_esphome_ota_component =
this;
85#ifdef USE_LWIP_FAST_SELECT
91#ifdef USE_OTA_PARTITIONS
98 "Over-The-Air updates:\n"
102#ifdef USE_OTA_PASSWORD
104 ESP_LOGCONFIG(TAG,
" Password configured");
107#ifdef USE_OTA_PARTITIONS
109 " Partition access allowed\n"
111 " Partition address: 0x%" PRIX32
"\n"
112 " Used size: %zu bytes (0x%zX)",
117 " Partition table:\n"
118 " %-12s %-4s %-8s %-10s %-10s",
119 "Name",
"Type",
"Subtype",
"Address",
"Size");
120 esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY,
nullptr);
121 while (it !=
nullptr) {
122 const esp_partition_t *partition = esp_partition_get(it);
123 ESP_LOGCONFIG(TAG,
" %-12s 0x%-2X 0x%-6X 0x%-8" PRIX32
" 0x%-8" PRIX32, partition->label, partition->type,
124 partition->subtype, partition->address, partition->size);
125 it = esp_partition_next(it);
127 esp_partition_iterator_release(it);
128 esp_bootloader_desc_t bootloader_desc;
129 esp_err_t err = esp_ota_get_bootloader_description(
nullptr, &bootloader_desc);
130 ESP_LOGCONFIG(TAG,
" Bootloader: ESP-IDF %s", (err == ESP_OK) ? bootloader_desc.idf_ver :
"version unknown");
147static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_COMPRESSION = 0x01;
148static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_SHA256_AUTH = 0x02;
149static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL = 0x04;
150static constexpr uint8_t SERVER_FEATURE_SUPPORTS_COMPRESSION = 0x01;
151static constexpr uint8_t SERVER_FEATURE_SUPPORTS_PARTITION_ACCESS = 0x02;
160 if (this->
client_ ==
nullptr) {
169 int err = this->
client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable,
sizeof(
int));
175 err = this->
client_->setblocking(
false);
190 ESP_LOGW(TAG,
"Handshake timeout");
198 if (!this->
try_read_(5, LOG_STR(
"read magic"))) {
203 static const uint8_t MAGIC_BYTES[5] = {0x6C, 0x26, 0xF7, 0x5C, 0x45};
205 ESP_LOGW(TAG,
"Magic bytes mismatch! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", this->
handshake_buf_[0],
220 if (!this->
try_write_(2, LOG_STR(
"ack magic"))) {
231 if (!this->
try_read_(1, LOG_STR(
"read feature"))) {
238 const bool supports_compression =
239 (this->
ota_features_ & CLIENT_FEATURE_SUPPORTS_COMPRESSION) != 0 && this->
backend_->supports_compression();
246 static_assert(
HANDSHAKE_BUF_SIZE >= 2,
"handshake_buf_ must hold the 2-byte extended-protocol feature ack");
248 this->
handshake_buf_[1] = (supports_compression ? SERVER_FEATURE_SUPPORTS_COMPRESSION : 0);
249#ifdef USE_OTA_PARTITIONS
250 this->
handshake_buf_[1] |= SERVER_FEATURE_SUPPORTS_PARTITION_ACCESS;
260 static constexpr size_t STANDARD_PROTO_ACK_SIZE = 1;
261 static constexpr size_t EXTENDED_PROTO_ACK_SIZE = 2;
262 const size_t ack_size = this->
extended_proto_ ? EXTENDED_PROTO_ACK_SIZE : STANDARD_PROTO_ACK_SIZE;
263 if (!this->
try_write_(ack_size, LOG_STR(
"ack feature"))) {
266#ifdef USE_OTA_PASSWORD
279#ifdef USE_OTA_PASSWORD
345 uint8_t buf[OTA_BUFFER_SIZE];
346 char *sbuf =
reinterpret_cast<char *
>(buf);
349#if USE_OTA_VERSION == 2
350 size_t size_acknowledged = 0;
357 this->
client_->setsockopt(SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(tv));
358 this->
client_->setsockopt(SOL_SOCKET, SO_SNDTIMEO, &tv,
sizeof(tv));
359 this->
client_->setblocking(
true);
372 ESP_LOGV(TAG,
"OTA type is 0x%02x", ota_type);
379 ota_size = (
static_cast<size_t>(buf[0]) << 24) | (
static_cast<size_t>(buf[1]) << 16) |
380 (
static_cast<size_t>(buf[2]) << 8) | buf[3];
381 ESP_LOGV(TAG,
"Size is %zu bytes", ota_size);
383#ifndef USE_OTA_PARTITIONS
396#ifdef USE_OTA_STATE_LISTENER
401 error_code = this->
backend_->begin(ota_size, ota_type);
414 ESP_LOGV(TAG,
"Update: Binary MD5 is %s", sbuf);
415 this->
backend_->set_update_md5(sbuf);
426 while (total < ota_size) {
427 if (
millis() - last_data_ms > OTA_SOCKET_TIMEOUT_DATA) {
428 ESP_LOGW(TAG,
"No data received for %u ms", (
unsigned) OTA_SOCKET_TIMEOUT_DATA);
432 size_t remaining = ota_size - total;
433 size_t requested = remaining < OTA_BUFFER_SIZE ? remaining : OTA_BUFFER_SIZE;
436 const int err = errno;
442 ESP_LOGW(TAG,
"Read err %d", err);
444 }
else if (read == 0) {
445 ESP_LOGW(TAG,
"Remote closed");
450 error_code = this->
backend_->write(buf, read);
452 ESP_LOGW(TAG,
"Flash write err %d", error_code);
456#if USE_OTA_VERSION == 2
457 while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) {
459 size_acknowledged += OTA_BLOCK_SIZE;
464 if (now - last_progress > 1000) {
466 float percentage = (total * 100.0f) / ota_size;
467 ESP_LOGD(TAG,
"Progress: %0.1f%%", percentage);
468#ifdef USE_OTA_STATE_LISTENER
481 ESP_LOGW(TAG,
"End update err %d", error_code);
496 ESP_LOGI(TAG,
"Update complete");
498#ifdef USE_OTA_STATE_LISTENER
502#ifdef USE_OTA_PARTITIONS
512 this->
write_byte_(
static_cast<uint8_t
>(error_code));
526#ifdef USE_OTA_STATE_LISTENER
534 while (
len - at > 0) {
536 if (now - start > OTA_SOCKET_TIMEOUT_DATA) {
537 ESP_LOGW(TAG,
"Timeout reading %zu bytes",
len);
543 const int err = errno;
545 ESP_LOGW(TAG,
"Read err %zu bytes, errno %d",
len, err);
548 }
else if (read == 0) {
549 ESP_LOGW(TAG,
"Remote closed");
564 while (
len - at > 0) {
566 if (now - start > OTA_SOCKET_TIMEOUT_DATA) {
567 ESP_LOGW(TAG,
"Timeout writing %zu bytes",
len);
573 const int err = errno;
575 ESP_LOGW(TAG,
"Write err %zu bytes, errno %d",
len, err);
594 ESP_LOGW(TAG,
"Socket %s: errno %d", LOG_STR_ARG(msg), errno);
600 char peername[socket::SOCKADDR_STR_LEN];
601 this->
client_->getpeername_to(peername);
602 ESP_LOGD(TAG,
"Starting %s from %s", LOG_STR_ARG(phase), peername);
606 ESP_LOGW(TAG,
"Remote closed at %s", LOG_STR_ARG(during));
652 this->handshake_buf_pos_ += read;
654 return this->handshake_buf_pos_ >= to_read;
666 this->handshake_buf_pos_ +=
written;
668 return this->handshake_buf_pos_ >= to_write;
679#ifdef USE_OTA_PASSWORD
692#ifdef USE_OTA_PASSWORD
696 bool client_supports_sha256 = (this->
ota_features_ & CLIENT_FEATURE_SUPPORTS_SHA256_AUTH) != 0;
699 if (!client_supports_sha256) {
733 const size_t hex_size = hasher.
get_size() * 2;
734 const size_t nonce_len = hasher.
get_size() / 4;
735 const size_t auth_buf_size = 1 + 3 * hex_size;
736 this->
auth_buf_ = std::make_unique<uint8_t[]>(auth_buf_size);
739 char *buf =
reinterpret_cast<char *
>(this->
auth_buf_.get() + 1);
740 if (!
random_bytes(
reinterpret_cast<uint8_t *
>(buf), nonce_len)) {
747 hasher.
add(buf, nonce_len);
752 ESP_LOGV(TAG,
"Auth: Nonce is %.*s", (
int) hex_size, buf);
757 const size_t to_write = 1 + hex_size;
765 this->auth_buf_pos_ +=
written;
768 if (this->auth_buf_pos_ < to_write) {
773 this->auth_buf_pos_ = 0;
779 const size_t to_read = hex_size * 2;
783 size_t cnonce_offset = 1 + hex_size;
791 this->auth_buf_pos_ += read;
794 if (this->auth_buf_pos_ < to_read) {
799 const char *nonce =
reinterpret_cast<char *
>(this->
auth_buf_.get() + 1);
800 const char *cnonce = nonce + hex_size;
801 const char *response = cnonce + hex_size;
808 hasher.
add(this->
password_.c_str(), this->password_.length());
809 hasher.
add(nonce, hex_size * 2);
812 ESP_LOGV(TAG,
"Auth: CNonce is %.*s", (
int) hex_size, cnonce);
813#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
816 ESP_LOGV(TAG,
"Auth: Result is %.*s", (
int) hex_size, computed_hash);
818 ESP_LOGV(TAG,
"Auth: Response is %.*s", (
int) hex_size, response);
void feed_wdt()
Feed the task watchdog.
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
void status_momentary_error(const char *name, uint32_t length=5000)
Set error status flag and automatically clear it after a timeout.
void status_set_warning()
void enable_loop_soon_any_context()
Thread and ISR-safe version of enable_loop() that can be called from any context.
void disable_loop()
Disable this component's loop.
void status_clear_warning()
bool would_block_(int error_code) const
uint32_t running_app_offset_
uint8_t handshake_buf_pos_
uint16_t get_port() const
static constexpr size_t SHA256_HEX_SIZE
void yield_and_feed_watchdog_()
bool writeall_(const uint8_t *buf, size_t len)
bool try_read_(size_t to_read, const LogString *desc)
ota::OTABackendPtr backend_
bool try_write_(size_t to_write, const LogString *desc)
void dump_config() override
std::unique_ptr< uint8_t[]> auth_buf_
bool handle_write_error_(ssize_t written, const LogString *desc)
void log_auth_warning_(const LogString *msg)
uint32_t client_connect_time_
float get_setup_priority() const override
void send_error_and_cleanup_(ota::OTAResponseTypes error)
bool handle_read_error_(ssize_t read, const LogString *desc)
void log_read_error_(const LogString *what)
bool readall_(uint8_t *buf, size_t len)
void set_port(uint16_t port)
Manually set the port OTA should listen on.
bool write_byte_(uint8_t byte)
uint8_t handshake_buf_[HANDSHAKE_BUF_SIZE]
static constexpr size_t HANDSHAKE_BUF_SIZE
void server_failed_(const LogString *msg)
void transition_ota_state_(OTAState next_state)
void cleanup_connection_()
socket::ListenSocket * server_
void log_remote_closed_(const LogString *during)
std::unique_ptr< socket::Socket > client_
void log_start_(const LogString *phase)
void log_socket_error_(const LogString *msg)
void get_hex(char *output)
Retrieve the hash as hex characters. Output buffer must hold get_size() * 2 + 1 bytes.
bool equals_hex(const char *expected)
Compare the hash against a provided hex-encoded hash.
void notify_state_(OTAState state, float progress, uint8_t error)
SHA256 hash implementation.
void calculate() override
size_t get_size() const override
Get the size of the hash in bytes (32 for SHA256)
void add(const uint8_t *data, size_t len) override
int setblocking(bool blocking)
bool ready() const
Check if the socket has buffered data ready to read.
int bind(const struct sockaddr *addr, socklen_t addrlen)
int setsockopt(int level, int optname, const void *optval, socklen_t optlen)
std::unique_ptr< BSDSocketImpl > accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen)
struct lwip_sock * esphome_lwip_get_sock(int fd)
Look up a LwIP socket struct from a file descriptor.
void esphome_fast_select_set_ota_listener_sock(struct lwip_sock *sock)
Set the listener netconn that the fast-select callback filters OTA wakes against.
ESPHOME_ALWAYS_INLINE const char * get_use_address()
Get the active network hostname.
@ OTA_TYPE_UPDATE_PARTITION_TABLE
void get_running_app_position(uint32_t &offset, size_t &size)
@ OTA_RESPONSE_UPDATE_PREPARE_OK
@ OTA_RESPONSE_SUPPORTS_COMPRESSION
@ OTA_RESPONSE_BIN_MD5_OK
@ OTA_RESPONSE_UPDATE_END_OK
@ OTA_RESPONSE_RECEIVE_OK
@ OTA_RESPONSE_FEATURE_FLAGS
@ OTA_RESPONSE_ERROR_UNSUPPORTED_OTA_TYPE
@ OTA_RESPONSE_ERROR_AUTH_INVALID
@ OTA_RESPONSE_ERROR_UNKNOWN
@ OTA_RESPONSE_REQUEST_SHA256_AUTH
@ OTA_RESPONSE_ERROR_MAGIC
std::unique_ptr< ArduinoLibreTinyOTABackend > make_ota_backend()
constexpr float AFTER_WIFI
For components that should be initialized after WiFi is connected.
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port)
Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
std::unique_ptr< ListenSocket > socket_ip_loop_monitored(int type, int protocol)
bool random_bytes(uint8_t *data, size_t len)
Generate len random bytes using the platform's secure RNG (hardware RNG or OS CSPRNG).
void esphome_wake_ota_component_any_context()
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.