Skip to content

Respect timeout with SSL #8899

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cores/esp8266/Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class Stream: public Print {

// parsing methods

void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
virtual void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
unsigned long getTimeout () const { return _timeout; }

bool find(const char *target); // reads data from the stream until the target string is found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,33 @@ void loop() {

std::unique_ptr<BearSSL::WiFiClientSecure> client(new BearSSL::WiFiClientSecure);

#if 1 // 1=secure, 0=insecure

client->setFingerprint(fingerprint_sni_cloudflaressl_com);

// date needs to be setup
configTime("UTC", "pool.ntp.org");
time_t now;
while (time(&now) < 24 * 3600) {
Serial.println("waiting for NTP time to be set...");
delay(1000);
}
Serial.printf("date: %s", ctime(&now));

#else
// Or, if you happy to ignore the SSL certificate, then use the following line instead:
// client->setInsecure();
client->setInsecure();
#endif

HTTPClient https;
https.setWallTime(10000); // do not exceed 10s while getting data
client->setWallTime(20000); // do not exceed 20s during handshake

// Try to reduce RAM footprint when SSL server allows it
constexpr int sslbufsize = 1024;
bool mfln = client->probeMaxFragmentLength(jigsaw_host, jigsaw_port, sslbufsize);
Serial.printf("Can reduce SSL footprint to %d bytes in RAM: %s\n", sslbufsize, mfln ? "yes" : "no");
if (mfln) { client->setBufferSizes(sslbufsize, sslbufsize); }

Serial.print("[HTTPS] begin...\n");
if (https.begin(*client, jigsaw_host, jigsaw_port)) { // HTTPS
Expand Down
20 changes: 6 additions & 14 deletions libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ void HTTPClient::disconnect(bool preserveClient)
}

if(_reuse && _canReuse) {
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp keep open for reuse\n");
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp kept open for reuse\n");
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][end] tcp stop\n");
if(_client) {
Expand Down Expand Up @@ -303,15 +303,12 @@ void HTTPClient::setAuthorization(String auth)
}

/**
* set the timeout for the TCP connection
* set the wall time for operations
* @param timeout unsigned int
*/
void HTTPClient::setTimeout(uint16_t timeout)
void HTTPClient::setWallTime(unsigned long wallTime)
{
_tcpTimeout = timeout;
if(connected()) {
_client->setTimeout(timeout);
}
_wallTime.reset(wallTime?: esp8266::polledTimeout::oneShotMs::neverExpires);
}

/**
Expand Down Expand Up @@ -794,8 +791,6 @@ bool HTTPClient::connect(void)
return false;
}

_client->setTimeout(_tcpTimeout);

if(!_client->connect(_host.c_str(), _port)) {
DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port);
return false;
Expand Down Expand Up @@ -879,7 +874,6 @@ bool HTTPClient::sendHeader(const char * type)
*/
int HTTPClient::handleHeaderResponse()
{

if(!connected()) {
return HTTPC_ERROR_NOT_CONNECTED;
}
Expand All @@ -891,16 +885,14 @@ int HTTPClient::handleHeaderResponse()
String transferEncoding;

_transferEncoding = HTTPC_TE_IDENTITY;
unsigned long lastDataTime = millis();
_wallTime.reset();

while(connected()) {
size_t len = _client->available();
if(len > 0) {
int headerSeparator = -1;
String headerLine = _client->readStringUntil('\n');

lastDataTime = millis();

DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] RX: '%s'\n", headerLine.c_str());

if (headerLine.startsWith(F("HTTP/1."))) {
Expand Down Expand Up @@ -978,7 +970,7 @@ int HTTPClient::handleHeaderResponse()
}

} else {
if((millis() - lastDataTime) > _tcpTimeout) {
if(_wallTime) {
return HTTPC_ERROR_READ_TIMEOUT;
}
esp_yield();
Expand Down
13 changes: 10 additions & 3 deletions libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <Arduino.h>
#include <StreamString.h>
#include <WiFiClient.h>
#include <PolledTimeout.h>

#include <memory>

Expand Down Expand Up @@ -152,7 +153,7 @@ typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
class HTTPClient
{
public:
HTTPClient() = default;
HTTPClient(): _wallTime(HTTPCLIENT_DEFAULT_TCP_TIMEOUT) {};
~HTTPClient() = default;
HTTPClient(HTTPClient&&) = default;
HTTPClient& operator=(HTTPClient&&) = default;
Expand All @@ -177,7 +178,8 @@ class HTTPClient
void setAuthorization(const char * user, const char * password);
void setAuthorization(const char * auth);
void setAuthorization(String auth);
void setTimeout(uint16_t timeout);
void setWallTime(unsigned long wallTime);
[[deprecated("use setWallTime() instead")]] void setTimeout(unsigned long wallTime) { setWallTime(wallTime); }

// Redirections
void setFollowRedirects(followRedirects_t follow);
Expand Down Expand Up @@ -256,7 +258,7 @@ class HTTPClient
String _host;
uint16_t _port = 0;
bool _reuse = true;
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
esp8266::polledTimeout::oneShotMs _wallTime;
bool _useHTTP10 = false;

String _uri;
Expand Down Expand Up @@ -314,6 +316,7 @@ int HTTPClient::writeToStream(S * output)
}
} else if(_transferEncoding == HTTPC_TE_CHUNKED) {
int size = 0;
_wallTime.reset();
while(1) {
if(!connected()) {
return returnError(HTTPC_ERROR_CONNECTION_LOST);
Expand Down Expand Up @@ -360,6 +363,10 @@ int HTTPClient::writeToStream(S * output)
return returnError(HTTPC_ERROR_READ_TIMEOUT);
}

if (_wallTime) {
return returnError(HTTPC_ERROR_READ_TIMEOUT);
}

esp_yield();
}
} else {
Expand Down
10 changes: 8 additions & 2 deletions libraries/ESP8266WiFi/src/WiFiClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size)
{
return 0;
}
_client->setTimeout(_timeout);
_client->setTimeout(_timeout); // context write uses timeout
return _client->write((const char*)buf, size);
}

Expand All @@ -238,7 +238,6 @@ size_t WiFiClient::write_P(PGM_P buf, size_t size)
{
return 0;
}
_client->setTimeout(_timeout);
StreamConstPtr nopeek(buf, size);
return nopeek.sendAll(this);
}
Expand Down Expand Up @@ -463,3 +462,10 @@ void WiFiClient::peekConsume (size_t consume)
if (_client)
_client->peekConsume(consume);
}

void WiFiClient::setTimeout (unsigned long timeout)
{
Client::setTimeout(timeout);
if (_client)
_client->setTimeout(timeout);
}
1 change: 1 addition & 0 deletions libraries/ESP8266WiFi/src/WiFiClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class WiFiClient : public Client, public SList<WiFiClient> {
// - https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rh-copy
virtual std::unique_ptr<WiFiClient> clone() const;

virtual void setTimeout (unsigned long timeout) override;
virtual uint8_t status();
virtual int connect(IPAddress ip, uint16_t port) override;
virtual int connect(const char *host, uint16_t port) override;
Expand Down
44 changes: 31 additions & 13 deletions libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ extern "C" {
}
#include "debug.h"
#include "ESP8266WiFi.h"
#include "PolledTimeout.h"
#include "WiFiClient.h"
#include "WiFiClientSecureBearSSL.h"
#include "StackThunk.h"
Expand Down Expand Up @@ -68,10 +67,9 @@ extern "C" {

namespace BearSSL {

void WiFiClientSecureCtx::_clear() {
// TLS handshake may take more than the 5 second default timeout
_timeout = 15000;
constexpr auto defaultWallTime = 10000UL;

void WiFiClientSecureCtx::_clear() {
_sc = nullptr;
_sc_svr = nullptr;
_eng = nullptr;
Expand Down Expand Up @@ -103,7 +101,7 @@ void WiFiClientSecureCtx::_clearAuthenticationSettings() {
}


WiFiClientSecureCtx::WiFiClientSecureCtx() : WiFiClient() {
WiFiClientSecureCtx::WiFiClientSecureCtx() : WiFiClient(), _wallTime(defaultWallTime) {
_clear();
_clearAuthenticationSettings();
_certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived
Expand All @@ -124,7 +122,7 @@ WiFiClientSecureCtx::~WiFiClientSecureCtx() {
WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext* client,
const X509List *chain, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta, int tls_min, int tls_max) {
const X509List *client_CA_ta, int tls_min, int tls_max): _wallTime(defaultWallTime) {
_clear();
_clearAuthenticationSettings();
stack_thunk_add_ref();
Expand All @@ -145,7 +143,7 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client,
const X509List *chain,
unsigned cert_issuer_key_type, const PrivateKey *sk,
int iobuf_in_size, int iobuf_out_size, ServerSessions *cache,
const X509List *client_CA_ta, int tls_min, int tls_max) {
const X509List *client_CA_ta, int tls_min, int tls_max): _wallTime(defaultWallTime) {
_clear();
_clearAuthenticationSettings();
stack_thunk_add_ref();
Expand Down Expand Up @@ -204,7 +202,10 @@ bool WiFiClientSecureCtx::stop(unsigned int maxWaitMs) {
}

bool WiFiClientSecureCtx::flush(unsigned int maxWaitMs) {
auto savedTimeout = _timeout;
_timeout = std::max(1U, maxWaitMs);
(void) _run_until(BR_SSL_SENDAPP);
_timeout = savedTimeout;
return WiFiClient::flush(maxWaitMs);
}

Expand Down Expand Up @@ -246,7 +247,6 @@ void WiFiClientSecureCtx::_freeSSL() {
_recvapp_len = 0;
// This connection is toast
_handshake_done = false;
_timeout = 15000;
}

bool WiFiClientSecureCtx::_clientConnected() {
Expand Down Expand Up @@ -462,7 +462,7 @@ size_t WiFiClientSecureCtx::peekBytes(uint8_t *buffer, size_t length) {
}

_startMillis = millis();
while ((_pollRecvBuffer() < (int) length) && ((millis() - _startMillis) < 5000)) {
while ((_pollRecvBuffer() < (int)length) && ((millis() - _startMillis) < _timeout)) {
yield();
}

Expand Down Expand Up @@ -539,6 +539,7 @@ int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) {
br_ssl_engine_sendrec_ack(_eng, wlen);
}
no_work = 0;
loopTimeout.reset();
continue;
}

Expand Down Expand Up @@ -582,6 +583,7 @@ int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) {
br_ssl_engine_recvrec_ack(_eng, rlen);
}
no_work = 0;
loopTimeout.reset();
continue;
}
}
Expand All @@ -602,6 +604,14 @@ int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) {
}

bool WiFiClientSecureCtx::_wait_for_handshake() {

#if defined(DEBUG_ESP_PORT)
if constexpr (F_CPU != 160000000L) {
DEBUG_ESP_PORT.printf_P((PGM_P)PSTR("BSSL: Please enable 160MHz build\n"));
}
#endif

_wallTime.reset();
_handshake_done = false;
while (!_handshake_done && _clientConnected()) {
int ret = _run_until(BR_SSL_SENDAPP);
Expand All @@ -612,6 +622,10 @@ bool WiFiClientSecureCtx::_wait_for_handshake() {
if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
_handshake_done = true;
}
if (_wallTime) {
DEBUG_BSSL("handshake too long\n");
break;
}
optimistic_yield(1000);
}
return _handshake_done;
Expand Down Expand Up @@ -1206,9 +1220,6 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) {
_x509_insecure = nullptr;
_x509_knownkey = nullptr;

// reduce timeout after successful handshake to fail fast if server stop accepting our data for whathever reason
if (ret) _timeout = 5000;

return ret;
}

Expand Down Expand Up @@ -1673,4 +1684,11 @@ bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint1
return _SendAbort(probe, supportsLen);
}

};
void WiFiClientSecure::setTimeout (unsigned long timeout)
{
WiFiClient::setTimeout(timeout);
if (_ctx)
_ctx->setTimeout(timeout);
}

}; // namespace BearSSL
12 changes: 12 additions & 0 deletions libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <bearssl/bearssl.h>
#include "BearSSLHelpers.h"
#include "CertStoreBearSSL.h"
#include "PolledTimeout.h"

namespace BearSSL {

Expand Down Expand Up @@ -147,6 +148,9 @@ class WiFiClientSecureCtx : public WiFiClient {
// consume bytes after use (see peekBuffer)
virtual void peekConsume (size_t consume) override;

// install a wall-time used during handshake
void setWallTime (unsigned long wallTime) { _wallTime.reset(wallTime?: esp8266::polledTimeout::oneShotMs::neverExpires); }

protected:
bool _connectSSL(const char *hostName); // Do initial SSL handshake

Expand Down Expand Up @@ -235,6 +239,8 @@ class WiFiClientSecureCtx : public WiFiClient {
bool _installServerX509Validator(const X509List *client_CA_ta); // Setup X509 client cert validation, if supplied

uint8_t *_streamLoad(Stream& stream, size_t size);

esp8266::polledTimeout::oneShotMs _wallTime;
}; // class WiFiClientSecureCtx


Expand Down Expand Up @@ -373,6 +379,12 @@ class WiFiClientSecure : public WiFiClient {

void disableKeepAlive() override { _ctx->disableKeepAlive(); };

// override setTimeout and forward to context
virtual void setTimeout (unsigned long timeout) override;

// install a wall-time used during handshake
void setWallTime (unsigned long wallTime) { _ctx->setWallTime(wallTime); }

private:
std::shared_ptr<WiFiClientSecureCtx> _ctx;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void setup() {

Serial.println();

ESPhttpUpdate.setClientTimeout(2000); // default was 8000
ESPhttpUpdate.setWallTime(2000); // default was 8000

WiFi.mode(WIFI_STA);
WiFiMulti.addAP(APSSID, APPSK);
Expand Down
Loading