diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index be67b0b6fe..6b3b316341 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -229,7 +229,10 @@ int main() ets_wdt_enable(); ets_putc('0'+res); ets_putc('\n'); - +#if 0 + //devyte: this verify step below (cmp:) only works when the end of copy operation above does not overwrite the + //beginning of the image in the empty area, see #7458. Disabling for now. + //TODO: replace the below verify with hash type, crc, or similar. // Verify the copy ets_putc('c'); ets_putc('m'); ets_putc('p'); ets_putc(':'); if (res == 0) { @@ -239,6 +242,7 @@ int main() } ets_putc('0'+res); ets_putc('\n'); +#endif if (res == 0) { cmd.action = ACTION_LOAD_APP; cmd.args[0] = cmd.args[1]; diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 1a6e5bc234..8c235c2087 100755 Binary files a/bootloaders/eboot/eboot.elf and b/bootloaders/eboot/eboot.elf differ diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 72cf07c896..0a16645d22 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -43,8 +43,6 @@ extern "C" { #define HIGH 0x1 #define LOW 0x0 -#define PWMRANGE 1023 - //GPIO FUNCTIONS #define INPUT 0x00 #define INPUT_PULLUP 0x02 @@ -176,6 +174,7 @@ int analogRead(uint8_t pin); void analogReference(uint8_t mode); void analogWrite(uint8_t pin, int val); void analogWriteFreq(uint32_t freq); +void analogWriteResolution(int res); void analogWriteRange(uint32_t range); unsigned long millis(void); diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 351a57746b..03bc5c3f8f 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -35,6 +35,7 @@ UpdaterClass::UpdaterClass() , _startAddress(0) , _currentAddress(0) , _command(U_FLASH) +, _ledPin(-1) , _hash(nullptr) , _verify(nullptr) , _progress_callback(nullptr) diff --git a/cores/esp8266/core_esp8266_features.h b/cores/esp8266/core_esp8266_features.h index d3b70f3dcc..5a2893a6ed 100644 --- a/cores/esp8266/core_esp8266_features.h +++ b/cores/esp8266/core_esp8266_features.h @@ -92,7 +92,19 @@ inline uint32_t esp_get_cycle_count() { __asm__ __volatile__("rsr %0,ccount":"=a"(ccount)); return ccount; } -#endif // not CORE_MOCK + +inline uint32_t esp_get_program_counter() __attribute__((always_inline)); +inline uint32_t esp_get_program_counter() { + uint32_t pc; + __asm__ __volatile__("movi %0, ." : "=r" (pc) : : ); // ©earlephilhower + return pc; +} + +#else // CORE_MOCK + +inline uint32_t esp_get_program_counter() { return 0; } + +#endif // CORE_MOCK // Tools for preloading code into the flash cache diff --git a/cores/esp8266/core_esp8266_wiring_pwm.cpp b/cores/esp8266/core_esp8266_wiring_pwm.cpp index 1ebdd64c89..b4991310d3 100644 --- a/cores/esp8266/core_esp8266_wiring_pwm.cpp +++ b/cores/esp8266/core_esp8266_wiring_pwm.cpp @@ -27,15 +27,21 @@ extern "C" { static uint32_t analogMap = 0; -static int32_t analogScale = PWMRANGE; +static int32_t analogScale = 255; // Match upstream default, breaking change from 2.x.x static uint16_t analogFreq = 1000; extern void __analogWriteRange(uint32_t range) { - if (range > 0) { + if ((range >= 15) && (range <= 65535)) { analogScale = range; } } +extern void __analogWriteResolution(int res) { + if ((res >= 4) && (res <= 16)) { + analogScale = (1 << res) - 1; + } +} + extern void __analogWriteFreq(uint32_t freq) { if (freq < 100) { analogFreq = 100; @@ -57,6 +63,10 @@ extern void __analogWrite(uint8_t pin, int val) { val = analogScale; } + // Per the Arduino docs at https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/ + // val: the duty cycle: between 0 (always off) and 255 (always on). + // So if val = 0 we have digitalWrite(LOW), if we have val==range we have digitalWrite(HIGH) + analogMap &= ~(1 << pin); uint32_t high = (analogPeriod * val) / analogScale; uint32_t low = analogPeriod - high; @@ -75,5 +85,6 @@ extern void __analogWrite(uint8_t pin, int val) { extern void analogWrite(uint8_t pin, int val) __attribute__((weak, alias("__analogWrite"))); extern void analogWriteFreq(uint32_t freq) __attribute__((weak, alias("__analogWriteFreq"))); extern void analogWriteRange(uint32_t range) __attribute__((weak, alias("__analogWriteRange"))); +extern void analogWriteResolution(int res) __attribute__((weak, alias("__analogWriteResolution"))); }; diff --git a/cores/esp8266/heap.cpp b/cores/esp8266/heap.cpp index 2001579ef0..3f549716f4 100644 --- a/cores/esp8266/heap.cpp +++ b/cores/esp8266/heap.cpp @@ -164,7 +164,7 @@ void ICACHE_RAM_ATTR print_loc(size_t size, const char* file, int line) if (inISR && (uint32_t)file >= 0x40200000) { DEBUG_HEAP_PRINTF("File: %p", file); } else if (!inISR && (uint32_t)file >= 0x40200000) { - char buf[ets_strlen(file)] __attribute__ ((aligned(4))); + char buf[ets_strlen(file) + 1] __attribute__((aligned(4))); ets_strcpy(buf, file); DEBUG_HEAP_PRINTF(buf); } else { diff --git a/cores/esp8266/umm_malloc/umm_local.c b/cores/esp8266/umm_malloc/umm_local.c index 8f83f4b4ac..392ef13c8f 100644 --- a/cores/esp8266/umm_malloc/umm_local.c +++ b/cores/esp8266/umm_malloc/umm_local.c @@ -206,7 +206,7 @@ int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) { the PROGMEM address must be word (4 bytes) aligned. The destination address for ets_memcpy must also be word-aligned. */ - char ram_buf[ets_strlen(fmt)] __attribute__ ((aligned(4))); + char ram_buf[ets_strlen(fmt) + 1] __attribute__((aligned(4))); ets_strcpy(ram_buf, fmt); va_list argPtr; va_start(argPtr, fmt); diff --git a/doc/ota_updates/readme.rst b/doc/ota_updates/readme.rst index 35eb931507..6714d30961 100755 --- a/doc/ota_updates/readme.rst +++ b/doc/ota_updates/readme.rst @@ -669,6 +669,8 @@ Updater is in the Core and deals with writing the firmware to the flash, checkin **Note:** The bootloader command will be stored into the first 128 bytes of user RTC memory, then it will be retrieved by eboot on boot. That means that user data present there will be lost `(per discussion in #5330) `__. +**Note:** For uncompressed firmware images, the Updater will change the flash mode bits if they differ from the flash mode the device is currently running at. This ensures that the flash mode is not changed to an incompatible mode when the device is in a remote or hard to access area. Compressed images are not modified, thus changing the flash mode in this instance could result in damage to the ESP8266 and/or flash memory chip or your device no longer be accessible via OTA, and requiring re-flashing via a serial connection `(per discussion in #7307) `__. + Update process - memory view ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/reference.rst b/doc/reference.rst index db789128e6..6cb03d6928 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -102,9 +102,19 @@ Analog output ``analogWrite(pin, value)`` enables software PWM on the given pin. PWM may be used on pins 0 to 16. Call ``analogWrite(pin, 0)`` to disable PWM -on the pin. ``value`` may be in range from 0 to ``PWMRANGE``, which is -equal to 1023 by default. PWM range may be changed by calling -``analogWriteRange(new_range)``. +on the pin. + +``value`` may be in range from 0 to 255 (which is the Arduino default). +PWM range may be changed by calling ``analogWriteRange(new_range)`` or +``analogWriteResolution(bits)``. ``new_range`` may be from 15...65535 +or ``bits`` may be from 4...16. + +**NOTE:** The default ``analogWrite`` range was 1023 in releases before +3.0, but this lead to incompatibility with external libraries which +depended on the Arduino core default of 256. Existing applications which +rely on the prior 1023 value may add a call to ``analogWriteRange(1023)`` +to their ``setup()`` routine to retrurn to their old behavior. Applications +which already were calling ``analogWriteRange`` need no change. PWM frequency is 1kHz by default. Call ``analogWriteFreq(new_frequency)`` to change the frequency. Valid values @@ -113,7 +123,7 @@ are from 100Hz up to 40000Hz. The ESP doesn't have hardware PWM, so the implementation is by software. With one PWM output at 40KHz, the CPU is already rather loaded. The more PWM outputs used, and the higher their frequency, the closer you get to -the CPU limits, and the less CPU cycles are available for sketch execution. +the CPU limits, and the fewer CPU cycles are available for sketch execution. Timing and delays ----------------- diff --git a/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino index 55009349fa..a69b9e35c6 100644 --- a/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino +++ b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino @@ -50,7 +50,7 @@ void loop() { Serial.print("[HTTPS] begin...\n"); // configure server and url - const uint8_t fingerprint[20] = {0xEB, 0xD9, 0xDF, 0x37, 0xC2, 0xCC, 0x84, 0x89, 0x00, 0xA0, 0x58, 0x52, 0x24, 0x04, 0xE4, 0x37, 0x3E, 0x2B, 0xF1, 0x41}; + const uint8_t fingerprint[20] = {0x15, 0x77, 0xdc, 0x04, 0x7c, 0x00, 0xf8, 0x70, 0x09, 0x34, 0x24, 0xf4, 0xd3, 0xa1, 0x7a, 0x6c, 0x1e, 0xa3, 0xe0, 0x2a}; client->setFingerprint(fingerprint); diff --git a/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino b/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino index 1335c347af..6715f9ee41 100644 --- a/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino +++ b/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino @@ -17,7 +17,7 @@ const int led = 13; void handleRoot() { digitalWrite(led, 1); - server.send(200, "text/plain", "hello from esp8266!"); + server.send(200, "text/plain", "hello from esp8266!\r\n"); digitalWrite(led, 0); } @@ -86,6 +86,66 @@ void setup(void) { server.onNotFound(handleNotFound); + ///////////////////////////////////////////////////////// + // Hook examples + + server.addHook([](const String & method, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction contentType) { + (void)method; // GET, PUT, ... + (void)url; // example: /root/myfile.html + (void)client; // the webserver tcp client connection + (void)contentType; // contentType(".html") => "text/html" + Serial.printf("A useless web hook has passed\n"); + Serial.printf("(this hook is in 0x%08x area (401x=IRAM 402x=FLASH))\n", esp_get_program_counter()); + return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE; + }); + + server.addHook([](const String&, const String & url, WiFiClient*, ESP8266WebServer::ContentTypeFunction) { + if (url.startsWith("/fail")) { + Serial.printf("An always failing web hook has been triggered\n"); + return ESP8266WebServer::CLIENT_MUST_STOP; + } + return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE; + }); + + server.addHook([](const String&, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction) { + if (url.startsWith("/dump")) { + Serial.printf("The dumper web hook is on the run\n"); + + // Here the request is not interpreted, so we cannot for sure + // swallow the exact amount matching the full request+content, + // hence the tcp connection cannot be handled anymore by the + // webserver. +#ifdef STREAMTO_API + // we are lucky + client->toWithTimeout(Serial, 500); +#else + auto last = millis(); + while ((millis() - last) < 500) { + char buf[32]; + size_t len = client->read((uint8_t*)buf, sizeof(buf)); + if (len > 0) { + Serial.printf("(<%d> chars)", (int)len); + Serial.write(buf, len); + last = millis(); + } + } +#endif + // Two choices: return MUST STOP and webserver will close it + // (we already have the example with '/fail' hook) + // or IS GIVEN and webserver will forget it + // trying with IS GIVEN and storing it on a dumb WiFiClient. + // check the client connection: it should not immediately be closed + // (make another '/dump' one to close the first) + Serial.printf("\nTelling server to forget this connection\n"); + static WiFiClient forgetme = *client; // stop previous one if present and transfer client refcounter + return ESP8266WebServer::CLIENT_IS_GIVEN; + } + return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE; + }); + + // Hook examples + ///////////////////////////////////////////////////////// + server.begin(); Serial.println("HTTP server started"); } diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index ddde5756e9..218fd168af 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -28,20 +28,12 @@ #include "FS.h" #include "detail/RequestHandlersImpl.h" -//#define DEBUG_ESP_HTTP_SERVER -#ifdef DEBUG_ESP_PORT -#define DEBUG_OUTPUT DEBUG_ESP_PORT -#else -#define DEBUG_OUTPUT Serial -#endif - static const char AUTHORIZATION_HEADER[] PROGMEM = "Authorization"; static const char qop_auth[] PROGMEM = "qop=auth"; static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\""; static const char WWW_Authenticate[] PROGMEM = "WWW-Authenticate"; static const char Content_Length[] PROGMEM = "Content-Length"; - template ESP8266WebServerTemplate::ESP8266WebServerTemplate(IPAddress addr, int port) : _server(addr, port) @@ -171,9 +163,7 @@ bool ESP8266WebServerTemplate::authenticateDigest(const String& user String authReq = header(FPSTR(AUTHORIZATION_HEADER)); if(authReq.startsWith(F("Digest"))) { authReq = authReq.substring(7); - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println(authReq); - #endif + DBGWS("%s\n", authReq.c_str()); String _username = _extractParam(authReq,F("username=\"")); if(!_username.length() || _username != String(username)) { authReq = ""; @@ -200,9 +190,7 @@ bool ESP8266WebServerTemplate::authenticateDigest(const String& user _nc = _extractParam(authReq, F("nc="), ','); _cnonce = _extractParam(authReq, F("cnonce=\"")); } - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Hash of user:realm:pass=" + H1); - #endif + DBGWS("Hash of user:realm:pass=%s\n", H1.c_str()); MD5Builder md5; md5.begin(); if(_currentMethod == HTTP_GET){ @@ -218,9 +206,7 @@ bool ESP8266WebServerTemplate::authenticateDigest(const String& user } md5.calculate(); String _H2 = md5.toString(); - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Hash of GET:uri=" + _H2); - #endif + DBGWS("Hash of GET:uri=%s\n", _H2.c_str()); md5.begin(); if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { md5.add(H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); @@ -229,9 +215,7 @@ bool ESP8266WebServerTemplate::authenticateDigest(const String& user } md5.calculate(); String _responsecheck = md5.toString(); - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("The Proper response=" +_responsecheck); - #endif + DBGWS("The Proper response=%s\n", _responsecheck.c_str()); if(_response == _responsecheck){ authReq = ""; return true; @@ -315,9 +299,7 @@ void ESP8266WebServerTemplate::handleClient() { return; } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("New client"); -#endif + DBGWS("New client\n"); _currentClient = client; _currentStatus = HC_WAIT_READ; @@ -327,6 +309,13 @@ void ESP8266WebServerTemplate::handleClient() { bool keepCurrentClient = false; bool callYield = false; + DBGWS("http-server loop: conn=%d avail=%d status=%s\n", + _currentClient.connected(), _currentClient.available(), + _currentStatus==HC_NONE?"none": + _currentStatus==HC_WAIT_READ?"wait-read": + _currentStatus==HC_WAIT_CLOSE?"wait-close": + "??"); + if (_currentClient.connected() || _currentClient.available()) { if (_currentClient.available() && _keepAlive) { _currentStatus = HC_WAIT_READ; @@ -339,34 +328,57 @@ void ESP8266WebServerTemplate::handleClient() { case HC_WAIT_READ: // Wait for data from client to become available if (_currentClient.available()) { - if (_parseRequest(_currentClient)) { + switch (_parseRequest(_currentClient)) + { + case CLIENT_REQUEST_CAN_CONTINUE: _currentClient.setTimeout(HTTP_MAX_SEND_WAIT); _contentLength = CONTENT_LENGTH_NOT_SET; _handleRequest(); - - if (_currentClient.connected()) { + /* fallthrough */ + case CLIENT_REQUEST_IS_HANDLED: + if (_currentClient.connected() || _currentClient.available()) { _currentStatus = HC_WAIT_CLOSE; _statusChange = millis(); keepCurrentClient = true; } - } - } else { // !_currentClient.available() + else + DBGWS("webserver: peer has closed after served\n"); + break; + case CLIENT_MUST_STOP: + DBGWS("Close client\n"); + _currentClient.stop(); + break; + case CLIENT_IS_GIVEN: + // client must not be stopped but must not be handled here anymore + // (example: tcp connection given to websocket) + DBGWS("Give client\n"); + break; + } // switch _parseRequest() + } else { + // !_currentClient.available(): waiting for more data if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { keepCurrentClient = true; } + else + DBGWS("webserver: closing after read timeout\n"); callYield = true; } break; case HC_WAIT_CLOSE: // Wait for client to close the connection - if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { + if (!_server.hasClient() && (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT)) { keepCurrentClient = true; callYield = true; + if (_currentClient.available()) + // continue serving current client + _currentStatus = HC_WAIT_READ; } - } + break; + } // switch _currentStatus } if (!keepCurrentClient) { + DBGWS("Drop client\n"); _currentClient = ClientType(); _currentStatus = HC_NONE; _currentUpload.reset(); @@ -687,17 +699,13 @@ template void ESP8266WebServerTemplate::_handleRequest() { bool handled = false; if (!_currentHandler){ -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("request handler not found"); -#endif + DBGWS("request handler not found\n"); } else { handled = _currentHandler->handle(*this, _currentMethod, _currentUri); -#ifdef DEBUG_ESP_HTTP_SERVER if (!handled) { - DEBUG_OUTPUT.println("request handler failed to handle request"); + DBGWS("request handler failed to handle request\n"); } -#endif } if (!handled && _notFoundHandler) { _notFoundHandler(); diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 2dc5201700..941be0b718 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -26,11 +26,24 @@ #include #include +#include #include #include #include "detail/mimetable.h" #include "Uri.h" +//#define DEBUG_ESP_HTTP_SERVER + +#ifdef DEBUG_ESP_HTTP_SERVER +#ifdef DEBUG_ESP_PORT +#define DBGWS(f,...) do { DEBUG_ESP_PORT.printf(PSTR(f), ##__VA_ARGS__); } while (0) +#else +#define DBGWS(f,...) do { Serial.printf(PSTR(f), ##__VA_ARGS__); } while (0) +#endif +#else +#define DBGWS(x...) do { (void)0; } while (0) +#endif + enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_HEAD, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS }; enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, UPLOAD_FILE_ABORTED }; @@ -80,6 +93,9 @@ class ESP8266WebServerTemplate using ClientType = typename ServerType::ClientType; using RequestHandlerType = RequestHandler; using WebServerType = ESP8266WebServerTemplate; + enum ClientFuture { CLIENT_REQUEST_CAN_CONTINUE, CLIENT_REQUEST_IS_HANDLED, CLIENT_MUST_STOP, CLIENT_IS_GIVEN }; + typedef String (*ContentTypeFunction) (const String&); + using HookFunction = std::function; void begin(); void begin(uint16_t port); @@ -200,11 +216,25 @@ class ESP8266WebServerTemplate static String responseCodeToString(const int code); + void addHook (HookFunction hook) { + if (_hook) { + auto previousHook = _hook; + _hook = [previousHook, hook](const String& method, const String& url, WiFiClient* client, ContentTypeFunction contentType) { + auto whatNow = previousHook(method, url, client, contentType); + if (whatNow == CLIENT_REQUEST_CAN_CONTINUE) + return hook(method, url, client, contentType); + return whatNow; + }; + } else { + _hook = hook; + } + } + protected: void _addRequestHandler(RequestHandlerType* handler); void _handleRequest(); void _finalizeResponse(); - bool _parseRequest(ClientType& client); + ClientFuture _parseRequest(ClientType& client); void _parseArguments(const String& data); int _parseArgumentsPrivate(const String& data, std::function handler); bool _parseForm(ClientType& client, const String& boundary, uint32_t len); @@ -261,8 +291,7 @@ class ESP8266WebServerTemplate String _sopaque; String _srealm; // Store the Auth realm between Calls - - + HookFunction _hook; }; diff --git a/libraries/ESP8266WebServer/src/Parsing-impl.h b/libraries/ESP8266WebServer/src/Parsing-impl.h index 8d3fda2782..4bb6db8878 100644 --- a/libraries/ESP8266WebServer/src/Parsing-impl.h +++ b/libraries/ESP8266WebServer/src/Parsing-impl.h @@ -25,13 +25,6 @@ #include "ESP8266WebServer.h" #include "detail/mimetable.h" -//#define DEBUG_ESP_HTTP_SERVER -#ifdef DEBUG_ESP_PORT -#define DEBUG_OUTPUT DEBUG_ESP_PORT -#else -#define DEBUG_OUTPUT Serial -#endif - #ifndef WEBSERVER_MAX_POST_ARGS #define WEBSERVER_MAX_POST_ARGS 32 #endif @@ -61,17 +54,14 @@ static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t } template -bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { +typename ESP8266WebServerTemplate::ClientFuture ESP8266WebServerTemplate::_parseRequest(ClientType& client) { // Read the first line of HTTP request String req = client.readStringUntil('\r'); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("request: "); - DEBUG_OUTPUT.println(req); -#endif + DBGWS("request: %s\n", req.c_str()); client.readStringUntil('\n'); //reset header value for (int i = 0; i < _headerKeysCount; ++i) { - _currentHeaders[i].value =String(); + _currentHeaders[i].value.clear(); } // First line of HTTP request looks like "GET /path HTTP/1.1" @@ -79,10 +69,8 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { int addr_start = req.indexOf(' '); int addr_end = req.indexOf(' ', addr_start + 1); if (addr_start == -1 || addr_end == -1) { -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Invalid request"); -#endif - return false; + DBGWS("Invalid request\n"); + return CLIENT_MUST_STOP; } String methodStr = req.substring(0, addr_start); @@ -98,6 +86,13 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { _currentUri = url; _chunked = false; + if (_hook) + { + auto whatNow = _hook(methodStr, url, &client, mime::getContentType); + if (whatNow != CLIENT_REQUEST_CAN_CONTINUE) + return whatNow; + } + HTTPMethod method = HTTP_GET; if (methodStr == F("HEAD")) { method = HTTP_HEAD; @@ -117,14 +112,8 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { _keepAlive = _currentVersion > 0; // Keep the connection alive by default // if the protocol version is greater than HTTP 1.0 -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("method: "); - DEBUG_OUTPUT.print(methodStr); - DEBUG_OUTPUT.print(" url: "); - DEBUG_OUTPUT.print(url); - DEBUG_OUTPUT.print(" search: "); - DEBUG_OUTPUT.println(searchStr); -#endif + DBGWS("method: %s url: %s search: %s keepAlive=: %d\n", + methodStr.c_str(), url.c_str(), searchStr.c_str(), _keepAlive); //attach handler RequestHandlerType* handler; @@ -157,12 +146,7 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { headerValue.trim(); _collectHeader(headerName.c_str(),headerValue.c_str()); - #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("headerName: "); - DEBUG_OUTPUT.println(headerName); - DEBUG_OUTPUT.print("headerValue: "); - DEBUG_OUTPUT.println(headerValue); - #endif + DBGWS("headerName: %s\nheaderValue: %s\n", headerName.c_str(), headerValue.c_str()); if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){ using namespace mime; @@ -193,7 +177,7 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { ) ) { - return false; + return CLIENT_MUST_STOP; } if (isEncoded) { @@ -218,7 +202,7 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { } else { // isForm is true // here: content is not yet read (plainBuf is still empty) if (!_parseForm(client, boundaryStr, contentLength)) { - return false; + return CLIENT_MUST_STOP; } } } else { @@ -237,12 +221,7 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { headerValue = req.substring(headerDiv + 2); _collectHeader(headerName.c_str(),headerValue.c_str()); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print(F("headerName: ")); - DEBUG_OUTPUT.println(headerName); - DEBUG_OUTPUT.print(F("headerValue: ")); - DEBUG_OUTPUT.println(headerValue); -#endif + DBGWS("headerName: %s\nheaderValue: %s\n", headerName.c_str(), headerValue.c_str()); if (headerName.equalsIgnoreCase(F("Host"))){ _hostHeader = headerValue; @@ -255,19 +234,15 @@ bool ESP8266WebServerTemplate::_parseRequest(ClientType& client) { client.flush(); #ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print(F("Request: ")); - DEBUG_OUTPUT.println(url); - DEBUG_OUTPUT.print(F("Arguments: ")); - DEBUG_OUTPUT.println(searchStr); - - DEBUG_OUTPUT.println(F("final list of key/value pairs:")); + DBGWS("Request: %s\nArguments: %s\nfinal list of key/value pairs:\n", + url.c_str(), searchStr.c_str()); for (int i = 0; i < _currentArgCount; i++) - DEBUG_OUTPUT.printf(" key:'%s' value:'%s'\r\n", + DBGWS(" key:'%s' value:'%s'\r\n", _currentArgs[i].key.c_str(), _currentArgs[i].value.c_str()); #endif - return true; + return CLIENT_REQUEST_CAN_CONTINUE; } template @@ -316,10 +291,7 @@ void ESP8266WebServerTemplate::_parseArguments(const String& data) { template int ESP8266WebServerTemplate::_parseArgumentsPrivate(const String& data, std::function handler) { -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args: "); - DEBUG_OUTPUT.println(data); -#endif + DBGWS("args: %s\n", data.c_str()); size_t pos = 0; int arg_total = 0; @@ -357,11 +329,7 @@ int ESP8266WebServerTemplate::_parseArgumentsPrivate(const String& d break; } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("args count: "); - DEBUG_OUTPUT.println(arg_total); -#endif - + DBGWS("args count: %d\n", (int)arg_total); return arg_total; } @@ -390,12 +358,7 @@ uint8_t ESP8266WebServerTemplate::_uploadReadByte(ClientType& client template bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const String& boundary, uint32_t len){ (void) len; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Parse Form: Boundary: "); - DEBUG_OUTPUT.print(boundary); - DEBUG_OUTPUT.print(" Length: "); - DEBUG_OUTPUT.println(len); -#endif + DBGWS("Parse Form: Boundary: '%s' Length: %d\n", boundary.c_str(), (int)len); String line; int retry = 0; do { @@ -429,18 +392,12 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const argFilename = argName.substring(nameStart+2, argName.length() - 1); argName = argName.substring(0, argName.indexOf('"')); argIsFile = true; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg FileName: "); - DEBUG_OUTPUT.println(argFilename); -#endif + DBGWS("PostArg FileName: %s\n", argFilename.c_str()); //use GET to set the filename if uploading using blob if (argFilename == F("blob") && hasArg(FPSTR(filename))) argFilename = arg(FPSTR(filename)); } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Name: "); - DEBUG_OUTPUT.println(argName); -#endif + DBGWS("PostArg Name: %s\n", argName.c_str()); using namespace mime; argType = FPSTR(mimeTable[txt].mimeType); line = client.readStringUntil('\r'); @@ -451,10 +408,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const client.readStringUntil('\r'); client.readStringUntil('\n'); } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Type: "); - DEBUG_OUTPUT.println(argType); -#endif + DBGWS("PostArg Type: %s\n", argType.c_str()); if (!argIsFile){ while(1){ line = client.readStringUntil('\r'); @@ -463,20 +417,14 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const if (argValue.length() > 0) argValue += '\n'; argValue += line; } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("PostArg Value: "); - DEBUG_OUTPUT.println(argValue); - DEBUG_OUTPUT.println(); -#endif + DBGWS("PostArg Value: %s\n\n", argValue.c_str()); RequestArgument& arg = _postArgs[_postArgsLen++]; arg.key = argName; arg.value = argValue; if (line == ("--"+boundary+"--")){ -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Done Parsing POST"); -#endif + DBGWS("Done Parsing POST\n"); break; } } else { @@ -488,12 +436,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const _currentUpload->totalSize = 0; _currentUpload->currentSize = 0; _currentUpload->contentLength = len; -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Start File: "); - DEBUG_OUTPUT.print(_currentUpload->filename); - DEBUG_OUTPUT.print(" Type: "); - DEBUG_OUTPUT.println(_currentUpload->type); -#endif + DBGWS("Start File: %s Type: %s\n", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->status = UPLOAD_FILE_WRITE; @@ -537,20 +480,14 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const _currentUpload->status = UPLOAD_FILE_END; if(_currentHandler && _currentHandler->canUpload(_currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("End File: "); - DEBUG_OUTPUT.print(_currentUpload->filename); - DEBUG_OUTPUT.print(" Type: "); - DEBUG_OUTPUT.print(_currentUpload->type); - DEBUG_OUTPUT.print(" Size: "); - DEBUG_OUTPUT.println(_currentUpload->totalSize); -#endif + DBGWS("End File: %s Type: %s Size: %d\n", + _currentUpload->filename.c_str(), + _currentUpload->type.c_str(), + (int)_currentUpload->totalSize); line = client.readStringUntil(0x0D); client.readStringUntil(0x0A); if (line == "--"){ -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.println("Done Parsing POST"); -#endif + DBGWS("Done Parsing POST\n"); break; } continue; @@ -598,10 +535,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const } return true; } -#ifdef DEBUG_ESP_HTTP_SERVER - DEBUG_OUTPUT.print("Error: line: "); - DEBUG_OUTPUT.println(line); -#endif + DBGWS("Error: line: %s\n", line.c_str()); return false; } diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino index 000dbb2a14..82374b40cc 100644 --- a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino @@ -7,11 +7,6 @@ esp8266/Arduino project continuous integration build. - Limitations: - only RSA certificates - no support of Perfect Forward Secrecy (PFS) - TLSv1.2 is supported since version 2.4.0-rc1 - Created by Ivan Grokhotkov, 2015. This example is in public domain. */ @@ -30,14 +25,38 @@ const char* password = STAPSK; const char* host = "api.github.com"; const int httpsPort = 443; -// Use web browser to view and copy -// SHA1 fingerprint of the certificate -const char fingerprint[] PROGMEM = "5F F1 60 31 09 04 3E F2 90 D2 B0 8A 50 38 04 E8 37 9F BC 76"; +// DigiCert High Assurance EV Root CA +const char trustRoot[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- +)EOF"; +X509List cert(trustRoot); void setup() { Serial.begin(115200); Serial.println(); - Serial.print("connecting to "); + Serial.print("Connecting to "); Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); @@ -50,21 +69,37 @@ void setup() { Serial.println("IP address: "); Serial.println(WiFi.localIP()); + // Set time via NTP, as required for x.509 validation + configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov"); + + Serial.print("Waiting for NTP time sync: "); + time_t now = time(nullptr); + while (now < 8 * 3600 * 2) { + delay(500); + Serial.print("."); + now = time(nullptr); + } + Serial.println(""); + struct tm timeinfo; + gmtime_r(&now, &timeinfo); + Serial.print("Current time: "); + Serial.print(asctime(&timeinfo)); + // Use WiFiClientSecure class to create TLS connection WiFiClientSecure client; - Serial.print("connecting to "); + Serial.print("Connecting to "); Serial.println(host); - Serial.printf("Using fingerprint '%s'\n", fingerprint); - client.setFingerprint(fingerprint); + Serial.printf("Using certificate: %s\n", trustRoot); + client.setTrustAnchors(&cert); if (!client.connect(host, httpsPort)) { - Serial.println("connection failed"); + Serial.println("Connection failed"); return; } String url = "/repos/esp8266/Arduino/commits/master/status"; - Serial.print("requesting URL: "); + Serial.print("Requesting URL: "); Serial.println(url); client.print(String("GET ") + url + " HTTP/1.1\r\n" + @@ -72,11 +107,11 @@ void setup() { "User-Agent: BuildFailureDetectorESP8266\r\n" + "Connection: close\r\n\r\n"); - Serial.println("request sent"); + Serial.println("Request sent"); while (client.connected()) { String line = client.readStringUntil('\n'); if (line == "\r") { - Serial.println("headers received"); + Serial.println("Headers received"); break; } } @@ -86,11 +121,11 @@ void setup() { } else { Serial.println("esp8266/Arduino CI has failed"); } - Serial.println("reply was:"); + Serial.println("Reply was:"); Serial.println("=========="); Serial.println(line); Serial.println("=========="); - Serial.println("closing connection"); + Serial.println("Closing connection"); } void loop() { diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index e026ea760a..abad142ed9 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -228,6 +228,16 @@ void ESP8266WiFiGenericClass::_eventCallback(void* arg) WiFiClient::stopAll(); } + if (event->event == EVENT_STAMODE_AUTHMODE_CHANGE) { + auto& src = event->event_info.auth_change; + if ((src.old_mode != AUTH_OPEN) && (src.new_mode == AUTH_OPEN)) { + // CVE-2020-12638 workaround. When we get a change to AUTH_OPEN from any other mode, drop the WiFi link because it's a downgrade attack + // TODO - When upgrading to 3.x.x with fix, remove this code + DEBUG_WIFI("WIFI_EVENT_STAMODE_AUTHMODE_CHANGE from encrypted(%d) to AUTH_OPEN, potential downgrade attack. Reconnecting WiFi. See CVE-2020-12638 for more details\n", src.old_mode); + WiFi.reconnect(); // Disconnects from STA and then reconnects + } + } + for(auto it = std::begin(sCbEventList); it != std::end(sCbEventList); ) { WiFiEventHandler &handler = *it; if (handler->canExpire() && handler.unique()) { diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 89621b3671..c94ac79e0f 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -105,6 +105,9 @@ WiFiClientSecure::WiFiClientSecure() : WiFiClient() { _clear(); _clearAuthenticationSettings(); _certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived + _sk = nullptr; + _axtls_chain = nullptr; + _axtls_sk = nullptr; stack_thunk_add_ref(); } diff --git a/package/build_boards_manager_package.sh b/package/build_boards_manager_package.sh index 5f459581df..05778d5487 100755 --- a/package/build_boards_manager_package.sh +++ b/package/build_boards_manager_package.sh @@ -1,25 +1,37 @@ #!/bin/bash -# Extract the release name from a release -# Default to draft tag name -ver=$(basename $(jq -e -r '.ref' "$GITHUB_EVENT_PATH")) -# If not available, try the publish tag name -if [ "$ver" == "null" ]; then - ver=$(jq -e -r '.release.tag_name' "$GITHUB_EVENT_PATH") -fi -# Fall back to the git description OTW (i.e. interactive) -if [ "$ver" == "null" ]; then - ver=$(git describe --tag) -fi -visiblever=$ver -plainver=$ver - -# Match 0.0.* as special-case early-access builds -if [ "${ver%.*}" = 0.0 ]; then - git tag -d ${ver} - ver=`git describe --tag HEAD` - plain_ver=$ver +if [ ! -z "${manualversion}" ]; then + + # manual-made release based on $manualversion + ver=${manualversion} + plain_ver=${ver} + visiblever=${ver} + [ -z "${REMOTE_URL}" ] && REMOTE_URL=https://github.com/esp8266/Arduino/releases/download + +else + + # Extract the release name from a release + + # Default to draft tag name + ver=$(basename $(jq -e -r '.ref' "$GITHUB_EVENT_PATH")) + # If not available, try the publish tag name + if [ "$ver" == "null" ]; then + ver=$(jq -e -r '.release.tag_name' "$GITHUB_EVENT_PATH") + fi + # Fall back to the git description OTW (i.e. interactive) + if [ "$ver" == "null" ]; then + ver=$(git describe --tag) + fi + visiblever=$ver + plainver=$ver + + # Match 0.0.* as special-case early-access builds + if [ "${ver%.*}" = 0.0 ]; then + git tag -d ${ver} + ver=`git describe --tag HEAD` + plain_ver=$ver + fi fi set -e