Skip to content

Commit edc3e72

Browse files
committed
fix(modem): Fixed OAT test to verify server cert and CN
1 parent f2223dd commit edc3e72

File tree

14 files changed

+314
-116
lines changed

14 files changed

+314
-116
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.
Binary file not shown.

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

Lines changed: 147 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,9 @@ static const char *TAG = "manual_ota";
1717
bool manual_ota::begin()
1818
{
1919
if (status != state::UNDEF) {
20-
ESP_LOGE(TAG, "Invalid state");
20+
ESP_LOGE(TAG, "Invalid state for manual_ota::perform");
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,40 @@ 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+
#ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT
35+
esp_transport_handle_t tcp = esp_transport_tcp_init();
36+
ssl_ = esp_transport_batch_tls_init(tcp, max_buffer_size_);
37+
http_.config_.transport = ssl_;
38+
if (!esp_transport_batch_set_ca_cert(ssl_, http_.config_.cert_pem, 0)) {
39+
return fail();
5140
}
52-
} else {
53-
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
41+
if (!esp_transport_batch_set_cn(ssl_, common_name_)) {
42+
return fail();
43+
}
44+
#else
45+
ESP_LOGE(TAG, "mode::BATCH Cannot be used without CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT");
5446
return false;
47+
#endif
48+
}
49+
50+
if (!http_.init()) {
51+
return fail();
52+
}
53+
54+
image_length_ = http_.get_image_len();
55+
if (image_length_ <= 0) {
56+
return fail();
5557
}
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_);
5958

6059
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;
60+
if (!http_.set_range(0, max_buffer_size_ - 1)) {
61+
return fail();
6662
}
67-
esp_http_client_set_header(http_, "Range", header_val);
68-
free(header_val);
6963
}
70-
esp_http_client_set_method(http_, HTTP_METHOD_GET);
64+
esp_http_client_set_method(http_.handle_, HTTP_METHOD_GET);
7165

7266
partition_ = esp_ota_get_next_update_partition(nullptr);
7367
if (partition_ == nullptr) {
@@ -86,40 +80,42 @@ bool manual_ota::begin()
8680
bool manual_ota::perform()
8781
{
8882
if (status != state::IMAGE_CHECK && status != state::START) {
89-
ESP_LOGE(TAG, "Invalid state");
83+
ESP_LOGE(TAG, "Invalid state for manual_ota::perform");
9084
return false;
9185
}
92-
esp_err_t err = esp_http_client_open(http_, 0);
86+
esp_err_t err = esp_http_client_open(http_.handle_, 0);
9387
if (err != ESP_OK) {
9488
if (image_length_ == file_length_) {
9589
status = state::END;
9690
return false;
9791
}
9892

99-
esp_http_client_close(http_);
93+
esp_http_client_close(http_.handle_);
10094
ESP_LOGI(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
10195
if (reconnect_attempts_++ < max_reconnect_attempts_) {
102-
if (prepare_reconnect()) {
103-
return true; // will retry in the next iteration
104-
}
96+
return true; // will retry in the next iteration
10597
}
106-
return fail_cleanup();
98+
return fail();
10799
}
108-
esp_http_client_fetch_headers(http_);
100+
esp_http_client_fetch_headers(http_.handle_);
109101

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();
102+
int batch_len = max_buffer_size_;
103+
if (mode_ == mode::BATCH) {
104+
batch_len = esp_transport_batch_tls_pre_read(ssl_, max_buffer_size_, timeout_ * 1000);
105+
if (batch_len < 0) {
106+
ESP_LOGE(TAG, "Error: Failed to pre-read plain text data!");
107+
fail();
108+
return false;
109+
}
114110
}
115111

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

118114
if (data_read < 0) {
119115
ESP_LOGE(TAG, "Error: SSL data read error");
120-
return fail_cleanup();
116+
return fail();
121117
} else if (data_read > 0) {
122-
esp_http_client_close(http_);
118+
esp_http_client_close(http_.handle_);
123119

124120
if (status == state::IMAGE_CHECK) {
125121
esp_app_desc_t new_app_info;
@@ -146,28 +142,26 @@ bool manual_ota::perform()
146142
ESP_LOGW(TAG, "New version is the same as invalid version.");
147143
ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
148144
ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
149-
return fail_cleanup();
145+
return fail();
150146
}
151147
}
152148

153149
status = state::START;
154150
err = esp_ota_begin(partition_, OTA_WITH_SEQUENTIAL_WRITES, &update_handle_);
155151
if (err != ESP_OK) {
156152
ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
157-
esp_ota_abort(update_handle_);
158-
return fail_cleanup();
153+
return fail();
159154
}
155+
ota_begin = true;
160156
ESP_LOGI(TAG, "esp_ota_begin succeeded");
161157
} else {
162158
ESP_LOGE(TAG, "Received chunk doesn't contain app descriptor");
163-
esp_ota_abort(update_handle_);
164-
return fail_cleanup();
159+
return fail();
165160
}
166161
}
167162
err = esp_ota_write(update_handle_, (const void *)buffer_.data(), data_read);
168163
if (err != ESP_OK) {
169-
esp_ota_abort(update_handle_);
170-
return fail_cleanup();
164+
return fail();
171165
}
172166
file_length_ += data_read;
173167
ESP_LOGI(TAG, "Written image length %d", file_length_);
@@ -178,28 +172,22 @@ bool manual_ota::perform()
178172
}
179173

180174
if (!prepare_reconnect()) {
181-
esp_ota_abort(update_handle_);
182-
return fail_cleanup();
175+
return fail();
183176
}
184177

185178
} else if (data_read == 0) {
186179
if (file_length_ == 0) {
187180
// 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();
181+
if (!http_.handle_redirects()) {
182+
return fail();
195183
}
196184

197-
err = esp_http_client_open(http_, 0);
185+
err = esp_http_client_open(http_.handle_, 0);
198186
if (err != ESP_OK) {
199187
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
200-
return fail_cleanup();
188+
return fail();
201189
}
202-
esp_http_client_fetch_headers(http_);
190+
esp_http_client_fetch_headers(http_.handle_);
203191
}
204192
}
205193

@@ -208,36 +196,23 @@ bool manual_ota::perform()
208196

209197
bool manual_ota::prepare_reconnect()
210198
{
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;
199+
esp_http_client_set_method(http_.handle_, HTTP_METHOD_GET);
200+
return http_.set_range(file_length_,
201+
(image_length_ - file_length_) > max_buffer_size_ ? (file_length_ + max_buffer_size_ - 1) : 0);
225202
}
226203

227-
bool manual_ota::fail_cleanup()
204+
bool manual_ota::fail()
228205
{
229-
esp_http_client_close(http_);
230-
esp_http_client_cleanup(http_);
231206
status = state::FAIL;
232207
return false;
233208
}
234209

235210
bool manual_ota::end()
236211
{
237212
if (status == state::END) {
238-
if (!esp_http_client_is_complete_data_received(http_)) {
213+
if (!http_.is_data_complete()) {
239214
ESP_LOGE(TAG, "Error in receiving complete file");
240-
return fail_cleanup();
215+
return fail();
241216
}
242217
esp_err_t err = esp_ota_end(update_handle_);
243218
if (err != ESP_OK) {
@@ -246,14 +221,97 @@ bool manual_ota::end()
246221
} else {
247222
ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
248223
}
249-
return fail_cleanup();
224+
return fail();
250225
}
226+
ota_begin = false;
251227
err = esp_ota_set_boot_partition(partition_);
252228
if (err != ESP_OK) {
253229
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
254-
return fail_cleanup();
230+
return fail();
255231
}
256232
return true;
257233
}
258234
return false;
259235
}
236+
237+
manual_ota::~manual_ota()
238+
{
239+
if (ota_begin) {
240+
esp_ota_abort(update_handle_);
241+
}
242+
}
243+
244+
bool manual_ota::http_client::handle_redirects()
245+
{
246+
int status_code = esp_http_client_get_status_code(handle_);
247+
ESP_LOGW(TAG, "Status code: %d", status_code);
248+
esp_err_t err = esp_http_client_set_redirection(handle_);
249+
if (err != ESP_OK) {
250+
ESP_LOGE(TAG, "URL redirection Failed");
251+
return false;
252+
}
253+
254+
err = esp_http_client_open(handle_, 0);
255+
if (err != ESP_OK) {
256+
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
257+
return false;
258+
}
259+
esp_http_client_fetch_headers(handle_);
260+
return true;
261+
}
262+
263+
bool manual_ota::http_client::set_range(size_t from, size_t to)
264+
{
265+
char *header_val = nullptr;
266+
if (to != 0) {
267+
asprintf(&header_val, "bytes=%d-%d", from, to);
268+
} else {
269+
asprintf(&header_val, "bytes=%d-", from);
270+
}
271+
if (header_val == nullptr) {
272+
ESP_LOGE(TAG, "Failed to allocate memory for HTTP header");
273+
return false;
274+
}
275+
esp_http_client_set_header(handle_, "Range", header_val);
276+
free(header_val);
277+
return true;
278+
}
279+
280+
bool manual_ota::http_client::is_data_complete()
281+
{
282+
return esp_http_client_is_complete_data_received(handle_);
283+
}
284+
285+
manual_ota::http_client::~http_client()
286+
{
287+
if (handle_) {
288+
esp_http_client_close(handle_);
289+
esp_http_client_cleanup(handle_);
290+
}
291+
}
292+
293+
bool manual_ota::http_client::init()
294+
{
295+
handle_ = esp_http_client_init(&config_);
296+
return handle_ != nullptr;
297+
}
298+
299+
int64_t manual_ota::http_client::get_image_len()
300+
{
301+
esp_http_client_set_method(handle_, HTTP_METHOD_HEAD);
302+
esp_err_t err = esp_http_client_perform(handle_);
303+
if (err == ESP_OK) {
304+
int http_status = esp_http_client_get_status_code(handle_);
305+
if (http_status != HttpStatus_Ok) {
306+
ESP_LOGE(TAG, "Received incorrect http status %d", http_status);
307+
return -1;
308+
}
309+
} else {
310+
ESP_LOGE(TAG, "ESP HTTP client perform failed: %d", err);
311+
return -1;
312+
}
313+
int64_t image_length = esp_http_client_get_content_length(handle_);
314+
ESP_LOGI(TAG, "image_length = %lld", image_length);
315+
esp_http_client_close(handle_);
316+
return image_length;
317+
}

0 commit comments

Comments
 (0)