Browse Source

Experimental support of HTTPUpdate for OTA (#1751)

- add httpupdate ota impelementation
- move async ota into a separate module
- support bearssl & axtls through wificlientsecure
- allow to disable arduino ide ota module
master
Max Prokhorov 5 years ago
committed by GitHub
parent
commit
9156eb1477
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 948 additions and 311 deletions
  1. +5
    -0
      code/espurna/config/dependencies.h
  2. +85
    -3
      code/espurna/config/general.h
  3. +1
    -0
      code/espurna/config/hardware.h
  4. +26
    -0
      code/espurna/config/progmem.h
  5. +36
    -1
      code/espurna/config/prototypes.h
  6. +22
    -1
      code/espurna/config/types.h
  7. +6
    -1
      code/espurna/espurna.ino
  8. +68
    -0
      code/espurna/libs/URL.h
  9. +0
    -298
      code/espurna/ota.ino
  10. +101
    -0
      code/espurna/ota_arduinoota.ino
  11. +227
    -0
      code/espurna/ota_asynctcp.ino
  12. +252
    -0
      code/espurna/ota_httpupdate.ino
  13. +42
    -0
      code/espurna/static/digicert_evroot_pem.h
  14. +46
    -0
      code/espurna/static/letsencrypt_isrgrootx1_pem.h
  15. +22
    -1
      code/espurna/terminal.ino
  16. +5
    -4
      code/espurna/utils.ino
  17. +4
    -2
      code/espurna/web.ino

+ 5
- 0
code/espurna/config/dependencies.h View File

@ -71,3 +71,8 @@
#undef NTP_SUPPORT
#define NTP_SUPPORT 1 // Scheduler needs NTP
#endif
#if (SECURE_CLIENT == SECURE_CLIENT_BEARSSL)
#undef OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE
#define OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE 0 // Use new HTTPUpdate API with BearSSL
#endif

+ 85
- 3
code/espurna/config/general.h View File

@ -682,19 +682,99 @@
#define SPIFFS_SUPPORT 0 // Do not add support for SPIFFS by default
#endif
// -----------------------------------------------------------------------------
// SSL Client ** EXPERIMENTAL **
// -----------------------------------------------------------------------------
#ifndef SECURE_CLIENT
#define SECURE_CLIENT SECURE_CLIENT_NONE // What variant of WiFiClient to use
// SECURE_CLIENT_NONE - No secure client support (default)
// SECURE_CLIENT_AXTLS - axTLS client secure support (All Core versions, ONLY TLS 1.1)
// SECURE_CLIENT_BEARSSL - BearSSL client secure support (starting with 2.5.0, TLS 1.2)
//
// axTLS marked for derecation since Arduino Core 2.4.2 and **will** be removed in the future
#endif
// Security check that is performed when the connection is established:
// SECURE_CLIENT_CHECK_CA - Use Trust Anchor / Root Certificate
// Supported only by the SECURE_CLIENT_BEARSSL
// (See respective ..._SECURE_CLIENT_INCLUDE_CA options per-module)
// SECURE_CLIENT_CHECK_FINGERPRINT - Check certificate fingerprint
// SECURE_CLIENT_CHECK_NONE - Allow insecure connections
#ifndef SECURE_CLIENT_CHECK
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
#define SECURE_CLIENT_CHECK SECURE_CLIENT_CHECK_CA
#else
#define SECURE_CLIENT_CHECK SECURE_CLIENT_CHECK_FINGERPRINT
#endif
#endif // SECURE_CLIENT_CHECK
// Support Maximum Fragment Length Negotiation TLS extension
// "...negotiate a smaller maximum fragment length due to memory limitations or bandwidth limitations."
// - https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/bearssl-client-secure-class.html#mfln-or-maximum-fragment-length-negotiation-saving-ram
// - https://tools.ietf.org/html/rfc6066#section-4
#ifndef SECURE_CLIENT_MFLN
#define SECURE_CLIENT_MFLN 0 // The only possible values are: 512, 1024, 2048 and 4096
// Set to 0 to disable (default)
#endif
// -----------------------------------------------------------------------------
// OTA
// -----------------------------------------------------------------------------
#ifndef OTA_PORT
#define OTA_PORT 8266 // OTA port
#define OTA_PORT 8266 // Port for ArduinoOTA
#endif
#ifndef OTA_MQTT_SUPPORT
#define OTA_MQTT_SUPPORT 0 // No support by default
#define OTA_MQTT_SUPPORT 0 // Listen for HTTP(s) URLs at '<root topic>/ota'. Depends on OTA_CLIENT
#endif
#ifndef OTA_ARDUINOOTA_SUPPORT
#define OTA_ARDUINOOTA_SUPPORT 1 // Support ArduinoOTA by default (4.2Kb)
// Implicitly depends on ESP8266mDNS library, thus increasing firmware size
#endif
#ifndef OTA_CLIENT
#define OTA_CLIENT OTA_CLIENT_ASYNCTCP // Terminal / MQTT OTA support
// OTA_CLIENT_ASYNCTCP (ESPAsyncTCP library)
// OTA_CLIENT_HTTPUPDATE (Arduino Core library)
#endif
#ifndef OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE
#define OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE 1 // Use old HTTPUpdate API by default
#endif
#define OTA_GITHUB_FP "CA:06:F5:6B:25:8B:7A:0D:4F:2B:05:47:09:39:47:86:51:15:19:84"
#ifndef OTA_FINGERPRINT
#define OTA_FINGERPRINT OTA_GITHUB_FP
#endif
#ifndef OTA_SECURE_CLIENT_CHECK
#define OTA_SECURE_CLIENT_CHECK SECURE_CLIENT_CHECK
#endif
#ifndef OTA_SECURE_CLIENT_MFLN
#define OTA_SECURE_CLIENT_MFLN SECURE_CLIENT_MFLN
#endif
#ifndef OTA_SECURE_CLIENT_INCLUDE_CA
#define OTA_SECURE_CLIENT_INCLUDE_CA 0 // Use user-provided CA. Only PROGMEM PEM option is supported.
// TODO: eventually should be replaced with pre-parsed structs, read directly from flash
// (ref: https://github.com/earlephilhower/bearssl-esp8266/pull/14)
//
// When enabled, current implementation includes "static/ota_secure_client_ca.h" with
// const char _ota_client_http_update_ca[] PROGMEM = "...PEM data...";
// By default, using DigiCert root in "static/digicert_evroot_pem.h" (for https://github.com)
#endif
#define OTA_GITHUB_FP "D7:9F:07:61:10:B3:92:93:E3:49:AC:89:84:5B:03:80:C1:9E:2F:8B"
// -----------------------------------------------------------------------------
// NOFUSS
@ -1330,6 +1410,8 @@
#endif
// Enable RCSwitch support
// Originally implemented for SONOFF BASIC
// https://tinkerman.cat/adding-rf-to-a-non-rf-itead-sonoff/
// Also possible to use with SONOFF RF BRIDGE, thanks to @wildwiz
// https://github.com/xoseperez/espurna/wiki/Hardware-Itead-Sonoff-RF-Bridge---Direct-Hack
#ifndef RFB_DIRECT


+ 1
- 0
code/espurna/config/hardware.h View File

@ -3975,6 +3975,7 @@
#define INFLUXDB_SUPPORT 1
#define IR_SUPPORT 1
#define RF_SUPPORT 1
#define OTA_MQTT_SUPPORT 1
#define RFB_DIRECT 1
#define RFB_RX_PIN 4


+ 26
- 0
code/espurna/config/progmem.h View File

@ -135,6 +135,32 @@ PROGMEM const char espurna_modules[] =
#endif
"";
PROGMEM const char espurna_ota_modules[] =
#if OTA_ARDUINOOTA_SUPPORT
"ARDUINO "
#endif
#if (OTA_CLIENT == OTA_CLIENT_ASYNCTCP)
"ASYNCTCP "
#endif
#if (OTA_CLIENT == OTA_CLIENT_HTTPUPDATE)
#if (SECURE_CLIENT == SECURE_CLIENT_NONE)
"*HTTPUPDATE "
#endif
#if (SECURE_CLIENT == SECURE_CLIENT_AXTLS)
"*HTTPUPDATE_AXTLS "
#endif
#if (SECURE_CLIENT == SECURE_CLIENT_BEARSSL)
"*HTTPUPDATE_BEARSSL "
#endif
#endif // OTA_CLIENT_HTTPUPDATE
#if OTA_MQTT_SUPPORT
"MQTT "
#endif
#if WEB_SUPPORT
"WEB "
#endif
"";
//--------------------------------------------------------------------------------
// Sensors
//--------------------------------------------------------------------------------


+ 36
- 1
code/espurna/config/prototypes.h View File

@ -1,6 +1,7 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <functional>
#include <memory>
#include <core_version.h>
extern "C" {
@ -168,7 +169,30 @@ void i2c_read_buffer(uint8_t address, uint8_t * buffer, size_t len);
// -----------------------------------------------------------------------------
// OTA
// -----------------------------------------------------------------------------
#include "ESPAsyncTCP.h"
#include <ArduinoOTA.h>
#if OTA_CLIENT == OTA_CLIENT_ASYNCTCP
#include <ESPAsyncTCP.h>
#endif
#if OTA_CLIENT == OTA_CLIENT_HTTPUPDATE
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#endif
#if SECURE_CLIENT != SECURE_CLIENT_NONE
#include <WiFiClientSecure.h>
#if OTA_SECURE_CLIENT_INCLUDE_CA
#include "static/ota_secure_client_ca.h"
#else
#include "static/digicert_evroot_pem.h"
#define _ota_client_http_update_ca _ssl_digicert_ev_root_ca
#endif
#endif // SECURE_CLIENT_SUPPORT
// -----------------------------------------------------------------------------
// RFM69
@ -285,3 +309,14 @@ bool wifiConnected();
// -----------------------------------------------------------------------------
#include "rtcmem.h"
// -----------------------------------------------------------------------------
// std::make_unique backport for C++11
// -----------------------------------------------------------------------------
#if 201103L >= __cplusplus
namespace std {
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
}
#endif

+ 22
- 1
code/espurna/config/types.h View File

@ -348,4 +348,25 @@
// Telnet server
//------------------------------------------------------------------------------
#define TELNET_SERVER_ASYNC 0
#define TELNET_SERVER_WIFISERVER 1
#define TELNET_SERVER_WIFISERVER 1
//------------------------------------------------------------------------------
// OTA Client (not related to the Web OTA support)
//------------------------------------------------------------------------------
#define OTA_CLIENT_NONE 0
#define OTA_CLIENT_ASYNCTCP 1
#define OTA_CLIENT_HTTPUPDATE 2
//------------------------------------------------------------------------------
// Secure Client
//------------------------------------------------------------------------------
#define SECURE_CLIENT_NONE 0
#define SECURE_CLIENT_AXTLS 1
#define SECURE_CLIENT_BEARSSL 2
#define SECURE_CLIENT_CHECK_NONE 0 // !!! INSECURE CONNECTION !!!
#define SECURE_CLIENT_CHECK_FINGERPRINT 1 // legacy fingerprint validation
#define SECURE_CLIENT_CHECK_CA 2 // set trust anchor from PROGMEM CA certificate

+ 6
- 1
code/espurna/espurna.ino View File

@ -110,10 +110,15 @@ void setup() {
info();
wifiSetup();
otaSetup();
#if OTA_ARDUINOOTA_SUPPORT
arduinoOtaSetup();
#endif
#if TELNET_SUPPORT
telnetSetup();
#endif
#if OTA_CLIENT != OTA_CLIENT_NONE
otaClientSetup();
#endif
// -------------------------------------------------------------------------
// Check if system is stable


+ 68
- 0
code/espurna/libs/URL.h View File

@ -0,0 +1,68 @@
// -----------------------------------------------------------------------------
// Parse char string as URL
//
// Adapted from HTTPClient::beginInternal()
// https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp
//
// -----------------------------------------------------------------------------
#pragma once
struct URL {
String value;
String protocol;
String host;
String path;
uint16_t port;
URL(const char* url) { init(url); }
URL(const String& url) { init(url); }
void init(String url);
};
void URL::init(String url) {
this->value = url;
// cut the protocol part
int index = url.indexOf("://");
if (index > 0) {
this->protocol = url.substring(0, index);
url.remove(0, (index + 3));
}
if (this->protocol == "http") {
this->port = 80;
} else if (this->protocol == "https") {
this->port = 443;
}
// cut the host part
String _host;
index = url.indexOf('/');
if (index >= 0) {
_host = url.substring(0, index);
} else {
_host = url;
}
// store the remaining part as path
if (index >= 0) {
url.remove(0, index);
this->path = url;
} else {
this->path = "/";
}
// separate host from port, when present
index = _host.indexOf(':');
if (index >= 0) {
this->port = _host.substring(index + 1).toInt();
this->host = _host.substring(0, index);
} else {
this->host = _host;
}
}

+ 0
- 298
code/espurna/ota.ino View File

@ -1,298 +0,0 @@
/*
OTA MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#include "ArduinoOTA.h"
// -----------------------------------------------------------------------------
// Arduino OTA
// -----------------------------------------------------------------------------
void _otaConfigure() {
ArduinoOTA.setPort(OTA_PORT);
ArduinoOTA.setHostname(getSetting("hostname").c_str());
#if USE_PASSWORD
ArduinoOTA.setPassword(getAdminPass().c_str());
#endif
}
void _otaLoop() {
ArduinoOTA.handle();
}
// -----------------------------------------------------------------------------
// Terminal OTA
// -----------------------------------------------------------------------------
#if TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
#include <ESPAsyncTCP.h>
AsyncClient * _ota_client;
char * _ota_host;
char * _ota_url;
unsigned int _ota_port = 80;
unsigned long _ota_size = 0;
const char OTA_REQUEST_TEMPLATE[] PROGMEM =
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: ESPurna\r\n"
"Connection: close\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 0\r\n\r\n\r\n";
void _otaFrom(const char * host, unsigned int port, const char * url) {
if (_ota_host) free(_ota_host);
if (_ota_url) free(_ota_url);
_ota_host = strdup(host);
_ota_url = strdup(url);
_ota_port = port;
_ota_size = 0;
if (_ota_client == NULL) {
_ota_client = new AsyncClient();
}
_ota_client->onDisconnect([](void *s, AsyncClient *c) {
DEBUG_MSG_P(PSTR("\n"));
if (Update.end(true)){
DEBUG_MSG_P(PSTR("[OTA] Success: %u bytes\n"), _ota_size);
deferredReset(100, CUSTOM_RESET_OTA);
} else {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
eepromRotate(true);
}
DEBUG_MSG_P(PSTR("[OTA] Disconnected\n"));
_ota_client->free();
delete _ota_client;
_ota_client = NULL;
free(_ota_host);
_ota_host = NULL;
free(_ota_url);
_ota_url = NULL;
}, 0);
_ota_client->onTimeout([](void *s, AsyncClient *c, uint32_t time) {
_ota_client->close(true);
}, 0);
_ota_client->onData([](void * arg, AsyncClient * c, void * data, size_t len) {
char * p = (char *) data;
if (_ota_size == 0) {
Update.runAsync(true);
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
}
p = strstr((char *)data, "\r\n\r\n") + 4;
len = len - (p - (char *) data);
}
if (!Update.hasError()) {
if (Update.write((uint8_t *) p, len) != len) {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
}
}
_ota_size += len;
DEBUG_MSG_P(PSTR("[OTA] Progress: %u bytes\r"), _ota_size);
delay(0);
}, NULL);
_ota_client->onConnect([](void * arg, AsyncClient * client) {
#if ASYNC_TCP_SSL_ENABLED
if (443 == _ota_port) {
uint8_t fp[20] = {0};
sslFingerPrintArray(getSetting("otafp", OTA_GITHUB_FP).c_str(), fp);
SSL * ssl = _ota_client->getSSL();
if (ssl_match_fingerprint(ssl, fp) != SSL_OK) {
DEBUG_MSG_P(PSTR("[OTA] Warning: certificate doesn't match\n"));
}
}
#endif
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
eepromRotate(false);
DEBUG_MSG_P(PSTR("[OTA] Downloading %s\n"), _ota_url);
char buffer[strlen_P(OTA_REQUEST_TEMPLATE) + strlen(_ota_url) + strlen(_ota_host)];
snprintf_P(buffer, sizeof(buffer), OTA_REQUEST_TEMPLATE, _ota_url, _ota_host);
client->write(buffer);
}, NULL);
#if ASYNC_TCP_SSL_ENABLED
bool connected = _ota_client->connect(host, port, 443 == port);
#else
bool connected = _ota_client->connect(host, port);
#endif
if (!connected) {
DEBUG_MSG_P(PSTR("[OTA] Connection failed\n"));
_ota_client->close(true);
}
}
void _otaFrom(String url) {
if (!url.startsWith("http://") && !url.startsWith("https://")) {
DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n"));
return;
}
// Port from protocol
unsigned int port = 80;
if (url.startsWith("https://")) port = 443;
url = url.substring(url.indexOf("/") + 2);
// Get host
String host = url.substring(0, url.indexOf("/"));
// Explicit port
int p = host.indexOf(":");
if (p > 0) {
port = host.substring(p + 1).toInt();
host = host.substring(0, p);
}
// Get URL
String uri = url.substring(url.indexOf("/"));
_otaFrom(host.c_str(), port, uri.c_str());
}
#endif // TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
#if TERMINAL_SUPPORT
void _otaInitCommands() {
terminalRegisterCommand(F("OTA"), [](Embedis* e) {
if (e->argc < 2) {
terminalError(F("Wrong arguments"));
} else {
terminalOK();
String url = String(e->argv[1]);
_otaFrom(url);
}
});
}
#endif // TERMINAL_SUPPORT
#if OTA_MQTT_SUPPORT
void _otaMQTTCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_OTA);
}
if (type == MQTT_MESSAGE_EVENT) {
// Match topic
String t = mqttMagnitude((char *) topic);
if (t.equals(MQTT_TOPIC_OTA)) {
DEBUG_MSG_P(PSTR("[OTA] Initiating from URL: %s\n"), payload);
_otaFrom(payload);
}
}
}
#endif // OTA_MQTT_SUPPORT
// -----------------------------------------------------------------------------
void otaSetup() {
_otaConfigure();
#if TERMINAL_SUPPORT
_otaInitCommands();
#endif
#if OTA_MQTT_SUPPORT
mqttRegister(_otaMQTTCallback);
#endif
// Main callbacks
espurnaRegisterLoop(_otaLoop);
espurnaRegisterReload(_otaConfigure);
// -------------------------------------------------------------------------
ArduinoOTA.onStart([]() {
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
eepromRotate(false);
// Because ArduinoOTA is synchronous, force backup right now instead of waiting for the next loop()
eepromBackup(0);
DEBUG_MSG_P(PSTR("[OTA] Start\n"));
#if WEB_SUPPORT
wsSend_P(PSTR("{\"message\": 2}"));
#endif
});
ArduinoOTA.onEnd([]() {
DEBUG_MSG_P(PSTR("\n"));
DEBUG_MSG_P(PSTR("[OTA] Done, restarting...\n"));
#if WEB_SUPPORT
wsSend_P(PSTR("{\"action\": \"reload\"}"));
#endif
deferredReset(100, CUSTOM_RESET_OTA);
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
static unsigned int _progOld;
unsigned int _prog = (progress / (total / 100));
if (_prog != _progOld) {
DEBUG_MSG_P(PSTR("[OTA] Progress: %u%%\r"), _prog);
_progOld = _prog;
}
});
ArduinoOTA.onError([](ota_error_t error) {
#if DEBUG_SUPPORT
DEBUG_MSG_P(PSTR("\n[OTA] Error #%u: "), error);
if (error == OTA_AUTH_ERROR) DEBUG_MSG_P(PSTR("Auth Failed\n"));
else if (error == OTA_BEGIN_ERROR) DEBUG_MSG_P(PSTR("Begin Failed\n"));
else if (error == OTA_CONNECT_ERROR) DEBUG_MSG_P(PSTR("Connect Failed\n"));
else if (error == OTA_RECEIVE_ERROR) DEBUG_MSG_P(PSTR("Receive Failed\n"));
else if (error == OTA_END_ERROR) DEBUG_MSG_P(PSTR("End Failed\n"));
#endif
eepromRotate(true);
});
ArduinoOTA.begin();
}

+ 101
- 0
code/espurna/ota_arduinoota.ino View File

@ -0,0 +1,101 @@
/*
ARDUINO OTA MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if OTA_ARDUINOOTA_SUPPORT
// TODO: allocate ArduinoOTAClass on-demand, stop using global instance
void _arduinoOtaConfigure() {
ArduinoOTA.setPort(OTA_PORT);
ArduinoOTA.setHostname(getSetting("hostname").c_str());
#if USE_PASSWORD
ArduinoOTA.setPassword(getAdminPass().c_str());
#endif
ArduinoOTA.begin();
}
void _arduinoOtaLoop() {
ArduinoOTA.handle();
}
void _arduinoOtaOnStart() {
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
eepromRotate(false);
// Because ArduinoOTA is synchronous, force backup right now instead of waiting for the next loop()
eepromBackup(0);
DEBUG_MSG_P(PSTR("[OTA] Start\n"));
#if WEB_SUPPORT
wsSend_P(PSTR("{\"message\": 2}"));
#endif
}
void _arduinoOtaOnEnd() {
DEBUG_MSG_P(PSTR("\n"));
DEBUG_MSG_P(PSTR("[OTA] Done, restarting...\n"));
#if WEB_SUPPORT
wsSend_P(PSTR("{\"action\": \"reload\"}"));
#endif
deferredReset(100, CUSTOM_RESET_OTA);
}
void _arduinoOtaOnProgress(unsigned int progress, unsigned int total) {
// Removed to avoid websocket ping back during upgrade (see #1574)
// TODO: implement as separate from debugging message
#if WEB_SUPPORT
if (wsConnected()) return;
#endif
static unsigned int _progOld;
unsigned int _prog = (progress / (total / 100));
if (_prog != _progOld) {
DEBUG_MSG_P(PSTR("[OTA] Progress: %u%%\r"), _prog);
_progOld = _prog;
}
}
void _arduinoOtaOnError(ota_error_t error) {
#if DEBUG_SUPPORT
DEBUG_MSG_P(PSTR("\n[OTA] Error #%u: "), error);
if (error == OTA_AUTH_ERROR) DEBUG_MSG_P(PSTR("Auth Failed\n"));
else if (error == OTA_BEGIN_ERROR) DEBUG_MSG_P(PSTR("Begin Failed\n"));
else if (error == OTA_CONNECT_ERROR) DEBUG_MSG_P(PSTR("Connect Failed\n"));
else if (error == OTA_RECEIVE_ERROR) DEBUG_MSG_P(PSTR("Receive Failed\n"));
else if (error == OTA_END_ERROR) DEBUG_MSG_P(PSTR("End Failed\n"));
#endif
eepromRotate(true);
}
void arduinoOtaSetup() {
espurnaRegisterLoop(_arduinoOtaLoop);
espurnaRegisterReload(_arduinoOtaConfigure);
ArduinoOTA.onStart(_arduinoOtaOnStart);
ArduinoOTA.onEnd(_arduinoOtaOnEnd);
ArduinoOTA.onError(_arduinoOtaOnError);
ArduinoOTA.onProgress(_arduinoOtaOnProgress);
_arduinoOtaConfigure();
}
#endif // OTA_ARDUINOOTA_SUPPORT

+ 227
- 0
code/espurna/ota_asynctcp.ino View File

@ -0,0 +1,227 @@
/*
ASYNC CLIENT OTA MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if OTA_CLIENT == OTA_CLIENT_ASYNCTCP
// -----------------------------------------------------------------------------
// Terminal OTA command
// -----------------------------------------------------------------------------
#if TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
#include <ESPAsyncTCP.h>
#include "libs/URL.h"
std::unique_ptr<AsyncClient> _ota_client = nullptr;
unsigned long _ota_size = 0;
bool _ota_connected = false;
std::unique_ptr<URL> _ota_url = nullptr;
const char OTA_REQUEST_TEMPLATE[] PROGMEM =
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: ESPurna\r\n"
"Connection: close\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 0\r\n\r\n\r\n";
void _otaClientOnDisconnect(void *s, AsyncClient *c) {
DEBUG_MSG_P(PSTR("\n"));
if (Update.end(true)){
DEBUG_MSG_P(PSTR("[OTA] Success: %u bytes\n"), _ota_size);
deferredReset(100, CUSTOM_RESET_OTA);
} else {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
eepromRotate(true);
}
DEBUG_MSG_P(PSTR("[OTA] Disconnected\n"));
_ota_connected = false;
_ota_url = nullptr;
_ota_client = nullptr;
}
void _otaClientOnTimeout(void *s, AsyncClient *c, uint32_t time) {
_ota_connected = false;
_ota_url = nullptr;
_ota_client->close(true);
}
void _otaClientOnData(void * arg, AsyncClient * c, void * data, size_t len) {
char * p = (char *) data;
if (_ota_size == 0) {
Update.runAsync(true);
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
c->close(true);
return;
}
p = strstr((char *)data, "\r\n\r\n") + 4;
len = len - (p - (char *) data);
}
if (!Update.hasError()) {
if (Update.write((uint8_t *) p, len) != len) {
#ifdef DEBUG_PORT
Update.printError(DEBUG_PORT);
#endif
c->close(true);
return;
}
}
_ota_size += len;
DEBUG_MSG_P(PSTR("[OTA] Progress: %u bytes\r"), _ota_size);
delay(0);
}
void _otaClientOnConnect(void *arg, AsyncClient *client) {
#if ASYNC_TCP_SSL_ENABLED
int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
if ((check == SECURE_CLIENT_CHECK_FINGERPRINT) && (443 == _ota_url->port)) {
uint8_t fp[20] = {0};
sslFingerPrintArray(getSetting("otafp", OTA_FINGERPRINT).c_str(), fp);
SSL * ssl = _ota_client->getSSL();
if (ssl_match_fingerprint(ssl, fp) != SSL_OK) {
DEBUG_MSG_P(PSTR("[OTA] Warning: certificate fingerpint doesn't match\n"));
client->close(true);
return;
}
}
#endif
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
eepromRotate(false);
DEBUG_MSG_P(PSTR("[OTA] Downloading %s\n"), _ota_url->path.c_str());
char buffer[strlen_P(OTA_REQUEST_TEMPLATE) + _ota_url->path.length() + _ota_url->host.length()];
snprintf_P(buffer, sizeof(buffer), OTA_REQUEST_TEMPLATE, _ota_url->path.c_str(), _ota_url->host.c_str());
client->write(buffer);
}
void _otaClientFrom(const String& url) {
if (_ota_connected) {
DEBUG_MSG_P(PSTR("[OTA] Already connected\n"));
return;
}
_ota_size = 0;
if (_ota_url) _ota_url = nullptr;
_ota_url = std::make_unique<URL>(url);
/*
DEBUG_MSG_P(PSTR("[OTA] proto:%s host:%s port:%u path:%s\n"),
_ota_url->protocol.c_str(),
_ota_url->host.c_str(),
_ota_url->port,
_ota_url->path.c_str()
);
*/
// we only support HTTP
if ((!_ota_url->protocol.equals("http")) && (!_ota_url->protocol.equals("https"))) {
DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n"));
_ota_url = nullptr;
return;
}
if (!_ota_client) {
_ota_client = std::make_unique<AsyncClient>();
}
_ota_client->onDisconnect(_otaClientOnDisconnect, nullptr);
_ota_client->onTimeout(_otaClientOnTimeout, nullptr);
_ota_client->onData(_otaClientOnData, nullptr);
_ota_client->onConnect(_otaClientOnConnect, nullptr);
#if ASYNC_TCP_SSL_ENABLED
_ota_connected = _ota_client->connect(_ota_url->host.c_str(), _ota_url->port, 443 == _ota_url->port);
#else
_ota_connected = _ota_client->connect(_ota_url->host.c_str(), _ota_url->port);
#endif
if (!_ota_connected) {
DEBUG_MSG_P(PSTR("[OTA] Connection failed\n"));
_ota_url = nullptr;
_ota_client->close(true);
}
}
#endif // TERMINAL_SUPPORT || OTA_MQTT_SUPPORT
#if TERMINAL_SUPPORT
void _otaClientInitCommands() {
terminalRegisterCommand(F("OTA"), [](Embedis* e) {
if (e->argc < 2) {
terminalError(F("OTA <url>"));
} else {
_otaClientFrom(String(e->argv[1]));
terminalOK();
}
});
}
#endif // TERMINAL_SUPPORT
#if OTA_MQTT_SUPPORT
void _otaClientMqttCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_OTA);
}
if (type == MQTT_MESSAGE_EVENT) {
String t = mqttMagnitude((char *) topic);
if (t.equals(MQTT_TOPIC_OTA)) {
DEBUG_MSG_P(PSTR("[OTA] Initiating from URL: %s\n"), payload);
_otaClientFrom(payload);
}
}
}
#endif // OTA_MQTT_SUPPORT
// -----------------------------------------------------------------------------
void otaClientSetup() {
#if TERMINAL_SUPPORT
_otaClientInitCommands();
#endif
#if (MQTT_SUPPORT && OTA_MQTT_SUPPORT)
mqttRegister(_otaClientMqttCallback);
#endif
}
#endif // OTA_CLIENT == OTA_CLIENT_ASYNCTCP

+ 252
- 0
code/espurna/ota_httpupdate.ino View File

@ -0,0 +1,252 @@
/*
HTTP(s) OTA MODULE
Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
// -----------------------------------------------------------------------------
// OTA by using Core's HTTP(s) updater
// -----------------------------------------------------------------------------
#if OTA_CLIENT == OTA_CLIENT_HTTPUPDATE
#include <memory>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#include "libs/URL.h"
void _otaClientRunUpdater(WiFiClient* client, const String& url, const String& fp = "") {
UNUSED(client);
UNUSED(fp);
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
eepromRotate(false);
DEBUG_MSG_P(PSTR("[OTA] Downloading %s ...\n"), url.c_str());
// TODO: support currentVersion (string arg after 'url')
// NOTE: ESPhttpUpdate.update(..., fp) will **always** fail with empty fingerprint
// NOTE: It is possible to support BearSSL with 2.4.2 by using uint8_t[20] instead of String for fingerprint argument
ESPhttpUpdate.rebootOnUpdate(false);
t_httpUpdate_return result = HTTP_UPDATE_NO_UPDATES;
// We expect both .update(url, "", String_fp) and .update(url) to survice until axTLS is removed from the Core
#if (SECURE_CLIENT == SECURE_CLIENT_AXTLS)
if (url.startsWith("https://")) {
result = ESPhttpUpdate.update(url, "", fp);
} else {
result = ESPhttpUpdate.update(url);
}
#elif OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE
result = ESPhttpUpdate.update(url);
#else
result = ESPhttpUpdate.update(*client, url);
#endif
switch (result) {
case HTTP_UPDATE_FAILED:
DEBUG_MSG_P(PSTR("[OTA] Update failed (error %d): %s\n"), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
eepromRotate(true);
break;
case HTTP_UPDATE_NO_UPDATES:
DEBUG_MSG_P(PSTR("[OTA] No updates"));
eepromRotate(true);
break;
case HTTP_UPDATE_OK:
DEBUG_MSG_P(PSTR("[OTA] Done, restarting..."));
deferredReset(500, CUSTOM_RESET_OTA); // wait a bit more than usual
break;
}
}
#if OTA_CLIENT_HTTPUPDATE_2_3_0_COMPATIBLE
void _otaClientFromHttp(const String& url) {
_otaClientRunUpdater(nullptr, url, "");
}
#else
void _otaClientFromHttp(const String& url) {
auto client = std::make_unique<WiFiClient>();
_otaClientRunUpdater(client.get(), url, "");
}
#endif
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
void _otaClientFromHttps(const String& url) {
int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
bool settime = (check == SECURE_CLIENT_CHECK_CA);
if (!ntpSynced() && settime) {
DEBUG_MSG_P(PSTR("[OTA] Time not synced!\n"));
return;
}
// unique_ptr self-destructs after exiting function scope
// create WiFiClient on heap to use less stack space
auto client = std::make_unique<BearSSL::WiFiClientSecure>();
if (check == SECURE_CLIENT_CHECK_NONE) {
DEBUG_MSG_P(PSTR("[OTA] !!! Connection will not be validated !!!\n"));
client->setInsecure();
}
if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
String fp_string = getSetting("otafp", OTA_FINGERPRINT);
if (!fp_string.length()) {
DEBUG_MSG_P(PSTR("[OTA] Requested fingerprint auth, but 'otafp' is not set\n"));
return;
}
uint8_t fp_bytes[20] = {0};
sslFingerPrintArray(fp_string.c_str(), fp_bytes);
client->setFingerprint(fp_bytes);
}
BearSSL::X509List *ca = nullptr;
if (check == SECURE_CLIENT_CHECK_CA) {
ca = new BearSSL::X509List(_ota_client_http_update_ca);
// because we do not support libc methods of getting time, force client to use ntpclientlib's current time
// XXX: local2utc method use is detrimental when DST happening. now() should be utc
client->setX509Time(ntpLocal2UTC(now()));
client->setTrustAnchors(ca);
}
// TODO: RX and TX buffer sizes must be equal?
const uint16_t requested_mfln = getSetting("otaScMFLN", OTA_SECURE_CLIENT_MFLN).toInt();
switch (requested_mfln) {
// default, do nothing
case 0:
break;
// match valid sizes only
case 512:
case 1024:
case 2048:
case 4096:
{
client->setBufferSizes(requested_mfln, requested_mfln);
break;
}
default:
DEBUG_MSG_P(PSTR("[OTA] Warning: MFLN buffer size must be one of 512, 1024, 2048 or 4096\n"));
}
_otaClientRunUpdater(client.get(), url);
}
#endif // SECURE_CLIENT_BEARSSL
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS
void _otaClientFromHttps(const String& url) {
const int check = getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK).toInt();
String fp_string;
if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
fp_string = getSetting("otafp", OTA_FINGERPRINT);
if (!fp_string.length() || !sslCheckFingerPrint(fp_string.c_str())) {
DEBUG_MSG_P(PSTR("[OTA] Wrong fingerprint\n"));
return;
}
}
_otaClientRunUpdater(nullptr, url, fp_string);
}
#endif // SECURE_CLIENT_AXTLS
void _otaClientFrom(const String& url) {
if (url.startsWith("http://")) {
_otaClientFromHttp(url);
return;
}
#if SECURE_CLIENT_SUPPORT
if (url.startsWith("https://")) {
_otaClientFromHttps(url);
return;
}
#endif
DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n"));
}
#if TERMINAL_SUPPORT
void _otaClientInitCommands() {
terminalRegisterCommand(F("OTA"), [](Embedis* e) {
if (e->argc < 2) {
terminalError(F("OTA <url>"));
} else {
_otaClientFrom(String(e->argv[1]));
terminalOK();
}
});
}
#endif // TERMINAL_SUPPORT
#if (MQTT_SUPPORT && OTA_MQTT_SUPPORT)
bool _ota_do_update = false;
String _ota_url;
void _otaClientLoop() {
if (_ota_do_update) {
_otaClientFrom(_ota_url);
_ota_do_update = false;
_ota_url = "";
}
}
void _otaClientMqttCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_OTA);
}
if (type == MQTT_MESSAGE_EVENT) {
String t = mqttMagnitude((char *) topic);
if (t.equals(MQTT_TOPIC_OTA)) {
DEBUG_MSG_P(PSTR("[OTA] Queuing from URL: %s\n"), payload);
_ota_do_update = true;
_ota_url = payload;
}
}
}
#endif // MQTT_SUPPORT
// -----------------------------------------------------------------------------
void otaClientSetup() {
#if TERMINAL_SUPPORT
_otaClientInitCommands();
#endif
#if (MQTT_SUPPORT && OTA_MQTT_SUPPORT)
mqttRegister(_otaClientMqttCallback);
espurnaRegisterLoop(_otaClientLoop);
#endif
}
#endif // OTA_CLIENT == OTA_CLIENT_HTTPUPDATE

+ 42
- 0
code/espurna/static/digicert_evroot_pem.h View File

@ -0,0 +1,42 @@
// https://github.com root issuer
// Issuer: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA
// Validity
// Not Before: Oct 22 12:00:00 2013 GMT
// Not After : Oct 22 12:00:00 2028 GMT
// Subject: C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 Extended Validation Server CA
#pragma once
#include <pgmspace.h>
const char PROGMEM _ssl_digicert_ev_root_ca[] = R"EOF(
-----BEGIN CERTIFICATE-----
MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW
YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY
uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/
LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy
/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh
cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k
8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy
dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2
MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j
b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW
gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh
hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg
4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa
2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs
1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1
oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn
8TUoE6smftX3eg==
-----END CERTIFICATE-----
)EOF";

+ 46
- 0
code/espurna/static/letsencrypt_isrgrootx1_pem.h View File

@ -0,0 +1,46 @@
// ISRG Root X1 (self-signed)
// from https://letsencrypt.org/certs/isrgrootx1.pem.txt
// Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1
// Validity
// Not Before: Jun 4 11:04:38 2015 GMT
// Not After : Jun 4 11:04:38 2035 GMT
// Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1
#pragma once
#include <pgmspace.h>
const char PROGMEM _ssl_letsencrypt_isrg_x1_ca[]= R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)EOF";

+ 22
- 1
code/espurna/terminal.ino View File

@ -204,7 +204,28 @@ void _terminalInitCommand() {
#if not SETTINGS_AUTOSAVE
terminalRegisterCommand(F("SAVE"), [](Embedis* e) {
eepromCommit();
DEBUG_MSG_P(PSTR("\n+OK\n"));
terminalOK();
});
#endif
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
terminalRegisterCommand(F("MFLN.PROBE"), [](Embedis* e) {
if (e->argc != 3) {
terminalError(F("[url] [value]"));
return;
}
URL _url(e->argv[1]);
uint16_t requested_mfln = atol(e->argv[2]);
auto client = std::make_unique<BearSSL::WiFiClientSecure>();
client->setInsecure();
if (client->probeMaxFragmentLength(_url.host.c_str(), _url.port, requested_mfln)) {
terminalOK();
} else {
terminalError(F("Buffer size not supported"));
}
});
#endif


+ 5
- 4
code/espurna/utils.ino View File

@ -68,6 +68,10 @@ String getEspurnaModules() {
return FPSTR(espurna_modules);
}
String getEspurnaOTAModules() {
return FPSTR(espurna_ota_modules);
}
#if SENSOR_SUPPORT
String getEspurnaSensors() {
return FPSTR(espurna_sensors);
@ -464,6 +468,7 @@ void info() {
DEBUG_MSG_P(PSTR("[MAIN] Board: %s\n"), getBoardName().c_str());
DEBUG_MSG_P(PSTR("[MAIN] Support: %s\n"), getEspurnaModules().c_str());
DEBUG_MSG_P(PSTR("[MAIN] OTA: %s\n"), getEspurnaOTAModules().c_str());
#if SENSOR_SUPPORT
DEBUG_MSG_P(PSTR("[MAIN] Sensors: %s\n"), getEspurnaSensors().c_str());
#endif // SENSOR_SUPPORT
@ -504,8 +509,6 @@ void info() {
// SSL
// -----------------------------------------------------------------------------
#if ASYNC_TCP_SSL_ENABLED
bool sslCheckFingerPrint(const char * fingerprint) {
return (strlen(fingerprint) == 59);
}
@ -541,8 +544,6 @@ bool sslFingerPrintChar(const char * fingerprint, char * destination) {
}
#endif
// -----------------------------------------------------------------------------
// Reset
// -----------------------------------------------------------------------------


+ 4
- 2
code/espurna/web.ino View File

@ -339,8 +339,10 @@ void _onUpgradeFile(AsyncWebServerRequest *request, String filename, size_t inde
#endif
}
} else {
//Removed to avoid websocket ping back during upgrade (see #1574)
//DEBUG_MSG_P(PSTR("[UPGRADE] Progress: %u bytes\r"), index + len);
// Removed to avoid websocket ping back during upgrade (see #1574)
// TODO: implement as separate from debugging message
if (wsConnected()) return;
DEBUG_MSG_P(PSTR("[UPGRADE] Progress: %u bytes\r"), index + len);
}
}


Loading…
Cancel
Save