diff --git a/mongoose.c b/mongoose.c index e0485eec..70471ada 100644 --- a/mongoose.c +++ b/mongoose.c @@ -10591,6 +10591,7 @@ void mg_tcpip_mapip(struct mg_connection *c, struct mg_addr *ip) { + #ifndef MG_OTA_MAX_URL_LEN #define MG_OTA_MAX_URL_LEN 256 #endif @@ -10611,6 +10612,17 @@ static struct mg_ota_state { static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data); +static bool s_tls_init(struct mg_connection *c, const char *url) { + struct mg_tls_opts opts = { + .ca = mg_str(MG_OTA_TLS_CA), .name = mg_url_host(url)}; + if (opts.ca.len == 0) { + mg_error(c, "MG_OTA_TLS_CA is not configured"); + } else { + mg_tls_init(c, &opts); + } + return c->is_tls_hs != 0 && !c->is_closing; +} + #if MG_ENABLE_CUSTOM_DEVICE_ID #else void mg_ota_device_id(char *buf, size_t len) { @@ -10643,6 +10655,11 @@ void mg_ota_device_id(char *buf, size_t len) { static void s_version_fn(struct mg_connection *c, int ev, void *ev_data) { uint64_t expiration = *(uint64_t *) c->data; + if (s_ota == NULL) { + c->fn = NULL; + c->is_closing = 1; + return; + } if (ev == MG_EV_POLL) { if (mg_millis() > expiration) mg_error(c, "Metadata timeout"); } else if (ev == MG_EV_CONNECT) { @@ -10650,6 +10667,9 @@ static void s_version_fn(struct mg_connection *c, int ev, void *ev_data) { struct mg_str host = mg_url_host(s_ota->json_url); const char *uri = mg_url_uri(s_ota->json_url); const char *sep = strchr(uri, '?') == NULL ? "?" : "&"; + if (mg_url_is_ssl(s_ota->json_url)) { + if (!s_tls_init(c, s_ota->json_url)) return; + } mg_ota_device_id(id, sizeof(id)); id[sizeof(id) - 1] = '\0'; mg_printf( @@ -10715,10 +10735,18 @@ static void status_fn_2(struct mg_connection *c, const char *errmsg) { static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) { uint64_t expiration = *(uint64_t *) c->data; + if (s_ota == NULL) { + c->fn = NULL; + c->is_closing = 1; + return; + } if (ev == MG_EV_POLL) { if (mg_millis() > expiration) mg_error(c, "OTA timeout"); } else if (ev == MG_EV_CONNECT) { struct mg_str host = mg_url_host(s_ota->url); + if (mg_url_is_ssl(s_ota->url)) { + if (!s_tls_init(c, s_ota->url)) return; + } mg_printf(c, "GET %s HTTP/1.1\r\n" "Host: %.*s\r\n" diff --git a/mongoose.h b/mongoose.h index d3f9856c..4f947b25 100644 --- a/mongoose.h +++ b/mongoose.h @@ -4628,7 +4628,7 @@ extern void mg_mqtt_poll(struct mg_mgr *); // // This is a low-level OTA API, not intended to be called directly by users. // Users are provided with a higher-level API: -// - defining MG_OTA_URL enables HTTP pull-based updates +// - defining MG_OTA_URL enables HTTP or HTTPS pull-based updates // - mg_http_start_ota() enables push-based updates // // However, it is possible to use the mg_ota_* API directly. Below is the @@ -4775,8 +4775,10 @@ enum { } \ } while (0) -// Pull-based OTA over HTTP. Fetches metadata_url, which must return a JSON -// object like: { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 } +// Pull-based OTA over HTTP or HTTPS. If it's HTTPS, MG_OTA_TLS_CA +// needs to be defined. Fetches metadata_url, which must +// return a JSON object like: +// { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 } // If the server version differs from MG_OTA_FIRMWARE_VERSION, downloads // FIRMWARE_URL and performs the OTA update. fn is called on every outcome: // NULL on successful flash, "Same version" when already up to date, or an @@ -4786,12 +4788,18 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url, void (*fn)(const char *error_message)); // Firmware info URL for mg_ota_poll() which calls mg_ota_url_check(). -// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/ +// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/ // Settable in mongoose_config.h #ifndef MG_OTA_URL #define MG_OTA_URL NULL #endif +// PEM or DER CA certificate used to authenticate an HTTPS OTA server. Set to +// NULL to use HTTP. Set in mongoose_config.h. +#ifndef MG_OTA_TLS_CA +#define MG_OTA_TLS_CA NULL +#endif + // OTA status callback function for mg_ota_poll() #ifndef MG_OTA_STATUS_FN #define MG_OTA_STATUS_FN NULL @@ -4821,8 +4829,9 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url, // E.g. on STM32, uses the 96-bit MCU UID converted to a hex string. void mg_ota_device_id(char *buf, size_t len); -// Checks for a firmware update over HTTP. Called automatically by mg_mgr_poll() -// when MG_OTA_URL is set in mongoose_config.h. Do not call directly. +// Checks for a firmware update over HTTP or HTTPS. Called automatically by +// mg_mgr_poll() when MG_OTA_URL is set in mongoose_config.h. Do not call +// directly. void mg_ota_poll(struct mg_mgr *); diff --git a/src/ota.c b/src/ota.c index ff25f922..3f57adcc 100644 --- a/src/ota.c +++ b/src/ota.c @@ -3,6 +3,7 @@ #include "json.h" #include "log.h" #include "printf.h" +#include "tls.h" #include "url.h" #include "util.h" @@ -26,6 +27,17 @@ static struct mg_ota_state { static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data); +static bool s_tls_init(struct mg_connection *c, const char *url) { + struct mg_tls_opts opts = { + .ca = mg_str(MG_OTA_TLS_CA), .name = mg_url_host(url)}; + if (opts.ca.len == 0) { + mg_error(c, "MG_OTA_TLS_CA is not configured"); + } else { + mg_tls_init(c, &opts); + } + return c->is_tls_hs != 0 && !c->is_closing; +} + #if MG_ENABLE_CUSTOM_DEVICE_ID #else void mg_ota_device_id(char *buf, size_t len) { @@ -58,6 +70,11 @@ void mg_ota_device_id(char *buf, size_t len) { static void s_version_fn(struct mg_connection *c, int ev, void *ev_data) { uint64_t expiration = *(uint64_t *) c->data; + if (s_ota == NULL) { + c->fn = NULL; + c->is_closing = 1; + return; + } if (ev == MG_EV_POLL) { if (mg_millis() > expiration) mg_error(c, "Metadata timeout"); } else if (ev == MG_EV_CONNECT) { @@ -65,6 +82,9 @@ static void s_version_fn(struct mg_connection *c, int ev, void *ev_data) { struct mg_str host = mg_url_host(s_ota->json_url); const char *uri = mg_url_uri(s_ota->json_url); const char *sep = strchr(uri, '?') == NULL ? "?" : "&"; + if (mg_url_is_ssl(s_ota->json_url)) { + if (!s_tls_init(c, s_ota->json_url)) return; + } mg_ota_device_id(id, sizeof(id)); id[sizeof(id) - 1] = '\0'; mg_printf( @@ -130,10 +150,18 @@ static void status_fn_2(struct mg_connection *c, const char *errmsg) { static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) { uint64_t expiration = *(uint64_t *) c->data; + if (s_ota == NULL) { + c->fn = NULL; + c->is_closing = 1; + return; + } if (ev == MG_EV_POLL) { if (mg_millis() > expiration) mg_error(c, "OTA timeout"); } else if (ev == MG_EV_CONNECT) { struct mg_str host = mg_url_host(s_ota->url); + if (mg_url_is_ssl(s_ota->url)) { + if (!s_tls_init(c, s_ota->url)) return; + } mg_printf(c, "GET %s HTTP/1.1\r\n" "Host: %.*s\r\n" diff --git a/src/ota.h b/src/ota.h index b99f9308..c8a861cd 100644 --- a/src/ota.h +++ b/src/ota.h @@ -2,7 +2,7 @@ // // This is a low-level OTA API, not intended to be called directly by users. // Users are provided with a higher-level API: -// - defining MG_OTA_URL enables HTTP pull-based updates +// - defining MG_OTA_URL enables HTTP or HTTPS pull-based updates // - mg_http_start_ota() enables push-based updates // // However, it is possible to use the mg_ota_* API directly. Below is the @@ -149,8 +149,10 @@ enum { } \ } while (0) -// Pull-based OTA over HTTP. Fetches metadata_url, which must return a JSON -// object like: { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 } +// Pull-based OTA over HTTP or HTTPS. If it's HTTPS, MG_OTA_TLS_CA +// needs to be defined. Fetches metadata_url, which must +// return a JSON object like: +// { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 } // If the server version differs from MG_OTA_FIRMWARE_VERSION, downloads // FIRMWARE_URL and performs the OTA update. fn is called on every outcome: // NULL on successful flash, "Same version" when already up to date, or an @@ -160,12 +162,18 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url, void (*fn)(const char *error_message)); // Firmware info URL for mg_ota_poll() which calls mg_ota_url_check(). -// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/ +// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/ // Settable in mongoose_config.h #ifndef MG_OTA_URL #define MG_OTA_URL NULL #endif +// PEM or DER CA certificate used to authenticate an HTTPS OTA server. Set to +// NULL to use HTTP. Set in mongoose_config.h. +#ifndef MG_OTA_TLS_CA +#define MG_OTA_TLS_CA NULL +#endif + // OTA status callback function for mg_ota_poll() #ifndef MG_OTA_STATUS_FN #define MG_OTA_STATUS_FN NULL @@ -195,6 +203,7 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url, // E.g. on STM32, uses the 96-bit MCU UID converted to a hex string. void mg_ota_device_id(char *buf, size_t len); -// Checks for a firmware update over HTTP. Called automatically by mg_mgr_poll() -// when MG_OTA_URL is set in mongoose_config.h. Do not call directly. +// Checks for a firmware update over HTTP or HTTPS. Called automatically by +// mg_mgr_poll() when MG_OTA_URL is set in mongoose_config.h. Do not call +// directly. void mg_ota_poll(struct mg_mgr *);