Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

267 lines
7.3 KiB

  1. /*
  2. HTTP(s) OTA MODULE
  3. Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  4. */
  5. // -----------------------------------------------------------------------------
  6. // OTA by using Core's HTTP(s) updater
  7. // -----------------------------------------------------------------------------
  8. #if OTA_CLIENT == OTA_CLIENT_HTTPUPDATE
  9. #include <memory>
  10. #include <ESP8266HTTPClient.h>
  11. #include <ESP8266httpUpdate.h>
  12. #include "libs/URL.h"
  13. #if SECURE_CLIENT != SECURE_CLIENT_NONE
  14. #if OTA_SECURE_CLIENT_INCLUDE_CA
  15. #include "static/ota_client_trusted_root_ca.h"
  16. #else
  17. #include "static/digicert_evroot_pem.h"
  18. #define _ota_client_trusted_root_ca _ssl_digicert_ev_root_ca
  19. #endif
  20. #endif // SECURE_CLIENT != SECURE_CLIENT_NONE
  21. void _otaClientRunUpdater(WiFiClient* client, const String& url, const String& fp = "") {
  22. UNUSED(client);
  23. UNUSED(fp);
  24. // Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
  25. eepromRotate(false);
  26. DEBUG_MSG_P(PSTR("[OTA] Downloading %s ...\n"), url.c_str());
  27. // TODO: support currentVersion (string arg after 'url')
  28. // NOTE: ESPhttpUpdate.update(..., fp) will **always** fail with empty fingerprint
  29. // NOTE: It is possible to support BearSSL with 2.4.2 by using uint8_t[20] instead of String for fingerprint argument
  30. ESPhttpUpdate.rebootOnUpdate(false);
  31. t_httpUpdate_return result = HTTP_UPDATE_NO_UPDATES;
  32. // We expect both .update(url, "", String_fp) and .update(url) to survice until axTLS is removed from the Core
  33. #if (SECURE_CLIENT == SECURE_CLIENT_AXTLS)
  34. if (url.startsWith("https://")) {
  35. result = ESPhttpUpdate.update(url, "", fp);
  36. } else {
  37. result = ESPhttpUpdate.update(url);
  38. }
  39. #elif OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE
  40. result = ESPhttpUpdate.update(url);
  41. #else
  42. result = ESPhttpUpdate.update(*client, url);
  43. #endif
  44. switch (result) {
  45. case HTTP_UPDATE_FAILED:
  46. DEBUG_MSG_P(PSTR("[OTA] Update failed (error %d): %s\n"), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
  47. eepromRotate(true);
  48. break;
  49. case HTTP_UPDATE_NO_UPDATES:
  50. DEBUG_MSG_P(PSTR("[OTA] No updates"));
  51. eepromRotate(true);
  52. break;
  53. case HTTP_UPDATE_OK:
  54. DEBUG_MSG_P(PSTR("[OTA] Done, restarting..."));
  55. deferredReset(500, CUSTOM_RESET_OTA); // wait a bit more than usual
  56. break;
  57. }
  58. }
  59. #if OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE
  60. void _otaClientFromHttp(const String& url) {
  61. _otaClientRunUpdater(nullptr, url, "");
  62. }
  63. #else
  64. void _otaClientFromHttp(const String& url) {
  65. auto client = std::make_unique<WiFiClient>();
  66. _otaClientRunUpdater(client.get(), url, "");
  67. }
  68. #endif
  69. #if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
  70. void _otaClientFromHttps(const String& url) {
  71. int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
  72. bool settime = (check == SECURE_CLIENT_CHECK_CA);
  73. if (!ntpSynced() && settime) {
  74. DEBUG_MSG_P(PSTR("[OTA] Time not synced!\n"));
  75. return;
  76. }
  77. // unique_ptr self-destructs after exiting function scope
  78. // create WiFiClient on heap to use less stack space
  79. auto client = std::make_unique<BearSSL::WiFiClientSecure>();
  80. if (check == SECURE_CLIENT_CHECK_NONE) {
  81. DEBUG_MSG_P(PSTR("[OTA] !!! Connection will not be validated !!!\n"));
  82. client->setInsecure();
  83. }
  84. if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
  85. String fp_string = getSetting("otaFP", OTA_FINGERPRINT);
  86. if (!fp_string.length()) {
  87. DEBUG_MSG_P(PSTR("[OTA] Requested fingerprint auth, but 'otaFP' is not set\n"));
  88. return;
  89. }
  90. uint8_t fp_bytes[20] = {0};
  91. sslFingerPrintArray(fp_string.c_str(), fp_bytes);
  92. client->setFingerprint(fp_bytes);
  93. }
  94. BearSSL::X509List *ca = nullptr;
  95. if (check == SECURE_CLIENT_CHECK_CA) {
  96. ca = new BearSSL::X509List(_ota_client_trusted_root_ca);
  97. // because we do not support libc methods of getting time, force client to use ntpclientlib's current time
  98. // XXX: local2utc method use is detrimental when DST happening. now() should be utc
  99. client->setX509Time(ntpLocal2UTC(now()));
  100. client->setTrustAnchors(ca);
  101. }
  102. // TODO: RX and TX buffer sizes must be equal?
  103. const uint16_t requested_mfln = getSetting("otaScMFLN", OTA_SECURE_CLIENT_MFLN).toInt();
  104. switch (requested_mfln) {
  105. // default, do nothing
  106. case 0:
  107. break;
  108. // match valid sizes only
  109. case 512:
  110. case 1024:
  111. case 2048:
  112. case 4096:
  113. {
  114. client->setBufferSizes(requested_mfln, requested_mfln);
  115. break;
  116. }
  117. default:
  118. DEBUG_MSG_P(PSTR("[OTA] Warning: MFLN buffer size must be one of 512, 1024, 2048 or 4096\n"));
  119. }
  120. _otaClientRunUpdater(client.get(), url);
  121. }
  122. #endif // SECURE_CLIENT_BEARSSL
  123. #if SECURE_CLIENT == SECURE_CLIENT_AXTLS
  124. void _otaClientFromHttps(const String& url) {
  125. const int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
  126. String fp_string;
  127. if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
  128. fp_string = getSetting("otaFP", OTA_FINGERPRINT);
  129. if (!fp_string.length() || !sslCheckFingerPrint(fp_string.c_str())) {
  130. DEBUG_MSG_P(PSTR("[OTA] Wrong fingerprint\n"));
  131. return;
  132. }
  133. }
  134. _otaClientRunUpdater(nullptr, url, fp_string);
  135. }
  136. #endif // SECURE_CLIENT_AXTLS
  137. void _otaClientFrom(const String& url) {
  138. if (url.startsWith("http://")) {
  139. _otaClientFromHttp(url);
  140. return;
  141. }
  142. #if SECURE_CLIENT != SECURE_CLIENT_NONE
  143. if (url.startsWith("https://")) {
  144. _otaClientFromHttps(url);
  145. return;
  146. }
  147. #endif
  148. DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n"));
  149. }
  150. #if TERMINAL_SUPPORT
  151. void _otaClientInitCommands() {
  152. terminalRegisterCommand(F("OTA"), [](Embedis* e) {
  153. if (e->argc < 2) {
  154. terminalError(F("OTA <url>"));
  155. } else {
  156. _otaClientFrom(String(e->argv[1]));
  157. terminalOK();
  158. }
  159. });
  160. }
  161. #endif // TERMINAL_SUPPORT
  162. #if (MQTT_SUPPORT && OTA_MQTT_SUPPORT)
  163. bool _ota_do_update = false;
  164. String _ota_url;
  165. void _otaClientLoop() {
  166. if (_ota_do_update) {
  167. _otaClientFrom(_ota_url);
  168. _ota_do_update = false;
  169. _ota_url = "";
  170. }
  171. }
  172. void _otaClientMqttCallback(unsigned int type, const char * topic, const char * payload) {
  173. if (type == MQTT_CONNECT_EVENT) {
  174. mqttSubscribe(MQTT_TOPIC_OTA);
  175. }
  176. if (type == MQTT_MESSAGE_EVENT) {
  177. String t = mqttMagnitude((char *) topic);
  178. if (t.equals(MQTT_TOPIC_OTA)) {
  179. DEBUG_MSG_P(PSTR("[OTA] Queuing from URL: %s\n"), payload);
  180. _ota_do_update = true;
  181. _ota_url = payload;
  182. }
  183. }
  184. }
  185. #endif // MQTT_SUPPORT
  186. // -----------------------------------------------------------------------------
  187. void otaClientSetup() {
  188. // Backwards compatibility
  189. moveSetting("otafp", "otaFP");
  190. #if TERMINAL_SUPPORT
  191. _otaClientInitCommands();
  192. #endif
  193. #if (MQTT_SUPPORT && OTA_MQTT_SUPPORT)
  194. mqttRegister(_otaClientMqttCallback);
  195. espurnaRegisterLoop(_otaClientLoop);
  196. #endif
  197. }
  198. #endif // OTA_CLIENT == OTA_CLIENT_HTTPUPDATE