Skip to content

Commit e281503

Browse files
committed
fix(modem): Fix ota-test to verify server cert and CN
1 parent 1bdf25c commit e281503

File tree

10 files changed

+286
-111
lines changed

10 files changed

+286
-111
lines changed

components/esp_modem/examples/modem_tcp_client/components/extra_tcp_transports/include/mbedtls_wrap.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Tls {
2929
int read(unsigned char *buf, size_t len);
3030
[[nodiscard]] bool set_own_cert(const_buf crt, const_buf key);
3131
[[nodiscard]] bool set_ca_cert(const_buf crt);
32+
bool set_hostname(const char *name);
3233
virtual int send(const unsigned char *buf, size_t len) = 0;
3334
virtual int recv(unsigned char *buf, size_t len) = 0;
3435
size_t get_available_bytes();

components/esp_modem/examples/modem_tcp_client/components/extra_tcp_transports/mbedtls_wrap.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ bool Tls::set_ca_cert(const_buf crt)
116116
return true;
117117
}
118118

119+
bool Tls::set_hostname(const char *name)
120+
{
121+
int ret = mbedtls_ssl_set_hostname(&ssl_, name);
122+
if (ret < 0) {
123+
print_error("mbedtls_ssl_set_hostname", ret);
124+
return false;
125+
}
126+
return true;
127+
}
128+
119129
Tls::Tls()
120130
{
121131
mbedtls_x509_crt_init(&public_cert_);

components/esp_modem/test/target_ota/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ sudo pppd /dev/ttyUSB1 115200 192.168.11.1:192.168.11.2 ms-dns 8.8.8.8 modem loc
1919
```
2020
* MQTT broker: Running mosquitto in the default config is enough, configuring the broker's URL to the local PPP address: `config.broker.address.uri = "mqtt://192.168.11.1";`
2121
* HTTP server: Need to support HTTP/1.1 (to support ranges). You can use the script `http_server.py` and configure the OTA endpoint as `"https://192.168.11.1:1234/esp32.bin"`
22+
23+
## Potential issues
24+
25+
When running this test it is expected to experience some buffer overflows or connection interruption.
26+
The modem library should recover from these failure modes and continue and complete OTA update.
27+
These issues are expected, since UART ISR is deliberately not placed into IRAM in the test configuration to exhibit some minor communication glitches.

components/esp_modem/test/target_ota/components/manual_ota/manual_ota.cpp

Lines changed: 142 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,6 @@ bool manual_ota::begin()
2020
ESP_LOGE(TAG, "Invalid state");
2121
return false;
2222
}
23-
status = state::INIT;
24-
esp_transport_handle_t tcp = esp_transport_tcp_init();
25-
ssl_ = esp_transport_batch_tls_init(tcp, max_buffer_size_);
26-
27-
esp_http_client_config_t config = { };
28-
config.skip_cert_common_name_check = true;
29-
config.url = uri_;
30-
config.transport = ssl_;
3123
const esp_partition_t *configured = esp_ota_get_boot_partition();
3224
const esp_partition_t *running = esp_ota_get_running_partition();
3325

@@ -36,38 +28,36 @@ bool manual_ota::begin()
3628
return false;
3729
}
3830

39-
http_ = esp_http_client_init(&config);
40-
if (http_ == nullptr) {
41-
ESP_LOGE(TAG, "Failed to initialise HTTP connection");
42-
return false;
43-
}
44-
esp_http_client_set_method(http_, HTTP_METHOD_HEAD);
45-
esp_err_t err = esp_http_client_perform(http_);
46-
if (err == ESP_OK) {
47-
int http_status = esp_http_client_get_status_code(http_);
48-
if (http_status != HttpStatus_Ok) {
49-
ESP_LOGE(TAG, "Received incorrect http status %d", http_status);
50-
return false;
31+
status = state::INIT;
32+
max_buffer_size_ = size_ * 1024;
33+
if (mode_ == mode::BATCH) {
34+
esp_transport_handle_t tcp = esp_transport_tcp_init();
35+
ssl_ = esp_transport_batch_tls_init(tcp, max_buffer_size_);
36+
http_.config_.transport = ssl_;
37+
if (!esp_transport_batch_set_ca_cert(ssl_, http_.config_.cert_pem, 0)) {
38+
return fail();
5139
}
52-
} else {
53-
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
54-
return false;
40+
if (!esp_transport_batch_set_cn(ssl_, common_name_)) {
41+
return fail();
42+
}
43+
44+
}
45+
46+
if (!http_.init()) {
47+
return fail();
48+
}
49+
50+
image_length_ = http_.get_image_len();
51+
if (image_length_ <= 0) {
52+
return fail();
5553
}
56-
image_length_ = esp_http_client_get_content_length(http_);
57-
ESP_LOGI(TAG, "image_length = %lld", image_length_);
58-
esp_http_client_close(http_);
5954

6055
if (image_length_ > size_) {
61-
char *header_val = nullptr;
62-
asprintf(&header_val, "bytes=0-%d", max_buffer_size_ - 1);
63-
if (header_val == nullptr) {
64-
ESP_LOGE(TAG, "Failed to allocate memory for HTTP header");
65-
return false;
56+
if (!http_.set_range(0, max_buffer_size_ - 1)) {
57+
return fail();
6658
}
67-
esp_http_client_set_header(http_, "Range", header_val);
68-
free(header_val);
6959
}
70-
esp_http_client_set_method(http_, HTTP_METHOD_GET);
60+
esp_http_client_set_method(http_.handle_, HTTP_METHOD_GET);
7161

7262
partition_ = esp_ota_get_next_update_partition(nullptr);
7363
if (partition_ == nullptr) {
@@ -89,37 +79,39 @@ bool manual_ota::perform()
8979
ESP_LOGE(TAG, "Invalid state");
9080
return false;
9181
}
92-
esp_err_t err = esp_http_client_open(http_, 0);
82+
esp_err_t err = esp_http_client_open(http_.handle_, 0);
9383
if (err != ESP_OK) {
9484
if (image_length_ == file_length_) {
9585
status = state::END;
9686
return false;
9787
}
9888

99-
esp_http_client_close(http_);
89+
esp_http_client_close(http_.handle_);
10090
ESP_LOGI(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
10191
if (reconnect_attempts_++ < max_reconnect_attempts_) {
102-
if (prepare_reconnect()) {
103-
return true; // will retry in the next iteration
104-
}
92+
return true; // will retry in the next iteration
10593
}
106-
return fail_cleanup();
94+
return fail();
10795
}
108-
esp_http_client_fetch_headers(http_);
96+
esp_http_client_fetch_headers(http_.handle_);
10997

110-
int batch_len = esp_transport_batch_tls_pre_read(ssl_, max_buffer_size_, timeout_ * 1000);
111-
if (batch_len < 0) {
112-
ESP_LOGE(TAG, "Error: Failed to pre-read plain text data!");
113-
return fail_cleanup();
98+
int batch_len = max_buffer_size_;
99+
if (mode_ == mode::BATCH) {
100+
batch_len = esp_transport_batch_tls_pre_read(ssl_, max_buffer_size_, timeout_ * 1000);
101+
if (batch_len < 0) {
102+
ESP_LOGE(TAG, "Error: Failed to pre-read plain text data!");
103+
fail();
104+
return false;
105+
}
114106
}
115107

116-
int data_read = esp_http_client_read(http_, buffer_.data(), batch_len);
108+
int data_read = esp_http_client_read(http_.handle_, buffer_.data(), batch_len);
117109

118110
if (data_read < 0) {
119111
ESP_LOGE(TAG, "Error: SSL data read error");
120-
return fail_cleanup();
112+
return fail();
121113
} else if (data_read > 0) {
122-
esp_http_client_close(http_);
114+
esp_http_client_close(http_.handle_);
123115

124116
if (status == state::IMAGE_CHECK) {
125117
esp_app_desc_t new_app_info;
@@ -146,28 +138,26 @@ bool manual_ota::perform()
146138
ESP_LOGW(TAG, "New version is the same as invalid version.");
147139
ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
148140
ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
149-
return fail_cleanup();
141+
return fail();
150142
}
151143
}
152144

153145
status = state::START;
154146
err = esp_ota_begin(partition_, OTA_WITH_SEQUENTIAL_WRITES, &update_handle_);
155147
if (err != ESP_OK) {
156148
ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
157-
esp_ota_abort(update_handle_);
158-
return fail_cleanup();
149+
return fail();
159150
}
151+
ota_begin = true;
160152
ESP_LOGI(TAG, "esp_ota_begin succeeded");
161153
} else {
162154
ESP_LOGE(TAG, "Received chunk doesn't contain app descriptor");
163-
esp_ota_abort(update_handle_);
164-
return fail_cleanup();
155+
return fail();
165156
}
166157
}
167158
err = esp_ota_write(update_handle_, (const void *)buffer_.data(), data_read);
168159
if (err != ESP_OK) {
169-
esp_ota_abort(update_handle_);
170-
return fail_cleanup();
160+
return fail();
171161
}
172162
file_length_ += data_read;
173163
ESP_LOGI(TAG, "Written image length %d", file_length_);
@@ -178,28 +168,22 @@ bool manual_ota::perform()
178168
}
179169

180170
if (!prepare_reconnect()) {
181-
esp_ota_abort(update_handle_);
182-
return fail_cleanup();
171+
return fail();
183172
}
184173

185174
} else if (data_read == 0) {
186175
if (file_length_ == 0) {
187176
// Try to handle possible HTTP redirections
188-
int status_code = esp_http_client_get_status_code(http_);
189-
ESP_LOGW(TAG, "Status code: %d", status_code);
190-
err = esp_http_client_set_redirection(http_);
191-
if (err != ESP_OK) {
192-
ESP_LOGE(TAG, "URL redirection Failed");
193-
esp_ota_abort(update_handle_);
194-
return fail_cleanup();
177+
if (!http_.handle_redirects()) {
178+
return fail();
195179
}
196180

197-
err = esp_http_client_open(http_, 0);
181+
err = esp_http_client_open(http_.handle_, 0);
198182
if (err != ESP_OK) {
199183
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
200-
return fail_cleanup();
184+
return fail();
201185
}
202-
esp_http_client_fetch_headers(http_);
186+
esp_http_client_fetch_headers(http_.handle_);
203187
}
204188
}
205189

@@ -208,36 +192,23 @@ bool manual_ota::perform()
208192

209193
bool manual_ota::prepare_reconnect()
210194
{
211-
esp_http_client_set_method(http_, HTTP_METHOD_GET);
212-
char *header_val = nullptr;
213-
if ((image_length_ - file_length_) > max_buffer_size_) {
214-
asprintf(&header_val, "bytes=%d-%d", file_length_, (file_length_ + max_buffer_size_ - 1));
215-
} else {
216-
asprintf(&header_val, "bytes=%d-", file_length_);
217-
}
218-
if (header_val == nullptr) {
219-
ESP_LOGE(TAG, "Failed to allocate memory for HTTP header");
220-
return false;
221-
}
222-
esp_http_client_set_header(http_, "Range", header_val);
223-
free(header_val);
224-
return true;
195+
esp_http_client_set_method(http_.handle_, HTTP_METHOD_GET);
196+
return http_.set_range(file_length_,
197+
(image_length_ - file_length_) > max_buffer_size_ ? (file_length_ + max_buffer_size_ - 1) : 0);
225198
}
226199

227-
bool manual_ota::fail_cleanup()
200+
bool manual_ota::fail()
228201
{
229-
esp_http_client_close(http_);
230-
esp_http_client_cleanup(http_);
231202
status = state::FAIL;
232203
return false;
233204
}
234205

235206
bool manual_ota::end()
236207
{
237208
if (status == state::END) {
238-
if (!esp_http_client_is_complete_data_received(http_)) {
209+
if (!http_.is_data_complete()) {
239210
ESP_LOGE(TAG, "Error in receiving complete file");
240-
return fail_cleanup();
211+
return fail();
241212
}
242213
esp_err_t err = esp_ota_end(update_handle_);
243214
if (err != ESP_OK) {
@@ -246,14 +217,97 @@ bool manual_ota::end()
246217
} else {
247218
ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
248219
}
249-
return fail_cleanup();
220+
return fail();
250221
}
222+
ota_begin = false;
251223
err = esp_ota_set_boot_partition(partition_);
252224
if (err != ESP_OK) {
253225
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
254-
return fail_cleanup();
226+
return fail();
255227
}
256228
return true;
257229
}
258230
return false;
259231
}
232+
233+
manual_ota::~manual_ota()
234+
{
235+
if (ota_begin) {
236+
esp_ota_abort(update_handle_);
237+
}
238+
}
239+
240+
bool manual_ota::http_client::handle_redirects()
241+
{
242+
int status_code = esp_http_client_get_status_code(handle_);
243+
ESP_LOGW(TAG, "Status code: %d", status_code);
244+
esp_err_t err = esp_http_client_set_redirection(handle_);
245+
if (err != ESP_OK) {
246+
ESP_LOGE(TAG, "URL redirection Failed");
247+
return false;
248+
}
249+
250+
err = esp_http_client_open(handle_, 0);
251+
if (err != ESP_OK) {
252+
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
253+
return false;
254+
}
255+
esp_http_client_fetch_headers(handle_);
256+
return true;
257+
}
258+
259+
bool manual_ota::http_client::set_range(size_t from, size_t to)
260+
{
261+
char *header_val = nullptr;
262+
if (to != 0) {
263+
asprintf(&header_val, "bytes=%d-%d", from, to);
264+
} else {
265+
asprintf(&header_val, "bytes=%d-", from);
266+
}
267+
if (header_val == nullptr) {
268+
ESP_LOGE(TAG, "Failed to allocate memory for HTTP header");
269+
return false;
270+
}
271+
esp_http_client_set_header(handle_, "Range", header_val);
272+
free(header_val);
273+
return true;
274+
}
275+
276+
bool manual_ota::http_client::is_data_complete()
277+
{
278+
return esp_http_client_is_complete_data_received(handle_);
279+
}
280+
281+
manual_ota::http_client::~http_client()
282+
{
283+
if (handle_) {
284+
esp_http_client_close(handle_);
285+
esp_http_client_cleanup(handle_);
286+
}
287+
}
288+
289+
bool manual_ota::http_client::init()
290+
{
291+
handle_ = esp_http_client_init(&config_);
292+
return handle_ != nullptr;
293+
}
294+
295+
int64_t manual_ota::http_client::get_image_len()
296+
{
297+
esp_http_client_set_method(handle_, HTTP_METHOD_HEAD);
298+
esp_err_t err = esp_http_client_perform(handle_);
299+
if (err == ESP_OK) {
300+
int http_status = esp_http_client_get_status_code(handle_);
301+
if (http_status != HttpStatus_Ok) {
302+
ESP_LOGE(TAG, "Received incorrect http status %d", http_status);
303+
return -1;
304+
}
305+
} else {
306+
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
307+
return -1;
308+
}
309+
int64_t image_length = esp_http_client_get_content_length(handle_);
310+
ESP_LOGI(TAG, "image_length = %lld", image_length);
311+
esp_http_client_close(handle_);
312+
return image_length;
313+
}

0 commit comments

Comments
 (0)