Skip to content

Commit c81f0f9

Browse files
committed
Introduced STATE_CLOSING for connections to prevent getting stuck in endless loop (see discussion in #3 for details)
1 parent e9ec021 commit c81f0f9

File tree

4 files changed

+78
-27
lines changed

4 files changed

+78
-27
lines changed

https/HTTPSConnection.cpp

+48-20
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ HTTPSConnection::HTTPSConnection(ResourceResolver * resResolver):
2525
_defaultHeaders = NULL;
2626
_isKeepAlive = false;
2727
_lastTransmissionTS = millis();
28+
_shutdownTS = 0;
2829
}
2930

3031
HTTPSConnection::~HTTPSConnection() {
@@ -115,29 +116,53 @@ bool HTTPSConnection::isError() {
115116
void HTTPSConnection::closeConnection() {
116117
// TODO: Call an event handler here, maybe?
117118

118-
// Tear down SSL
119-
if (_ssl) {
120-
// wait here so that SSL_shutdown returns 1
121-
while(SSL_shutdown(_ssl) == 0) delay(1);
122-
SSL_free(_ssl);
123-
_ssl = NULL;
124-
}
125119

126-
// Tear down the socket
127-
if (_socket >= 0) {
128-
HTTPS_DLOGHEX("[<--] Connection has been closed. fid = ", _socket);
129-
close(_socket);
130-
_socket = -1;
131-
_addrLen = 0;
132-
}
120+
if (_connectionState != STATE_ERROR && _connectionState != STATE_CLOSED) {
133121

134-
if (_connectionState != STATE_ERROR) {
135-
_connectionState = STATE_CLOSED;
136-
}
122+
// First call to closeConnection - set the timestamp to calculate the timeout later on
123+
if (_connectionState != STATE_CLOSING) {
124+
_shutdownTS = millis();
125+
}
137126

138-
if (_httpHeaders != NULL) {
139-
delete _httpHeaders;
140-
_httpHeaders = NULL;
127+
// Set the connection state to closing. We stay in closing as long as SSL has not been shutdown
128+
// correctly
129+
_connectionState = STATE_CLOSING;
130+
131+
// Try to tear down SSL
132+
if (_ssl) {
133+
if(SSL_shutdown(_ssl) == 0) {
134+
// SSL_shutdown will return 1 as soon as the client answered with close notify
135+
// This means we are safe to close the socket
136+
SSL_free(_ssl);
137+
_ssl = NULL;
138+
} else if (_shutdownTS + HTTPS_SHUTDOWN_TIMEOUT < millis()) {
139+
// The timeout has been hit, we force SSL shutdown now by freeing the context
140+
SSL_free(_ssl);
141+
_ssl = NULL;
142+
HTTPS_DLOG("[ERR] SSL_shutdown did not receive close notification from the client");
143+
_connectionState = STATE_ERROR;
144+
}
145+
}
146+
147+
// If SSL has been brought down, close the socket
148+
if (!_ssl) {
149+
// Tear down the socket
150+
if (_socket >= 0) {
151+
HTTPS_DLOGHEX("[<--] Connection has been closed. fid = ", _socket);
152+
close(_socket);
153+
_socket = -1;
154+
_addrLen = 0;
155+
}
156+
157+
if (_connectionState != STATE_ERROR) {
158+
_connectionState = STATE_CLOSED;
159+
}
160+
161+
if (_httpHeaders != NULL) {
162+
delete _httpHeaders;
163+
_httpHeaders = NULL;
164+
}
165+
}
141166
}
142167
}
143168

@@ -489,6 +514,9 @@ void HTTPSConnection::loop() {
489514
case STATE_BODY_FINISHED: // Request is complete
490515
closeConnection();
491516
break;
517+
case STATE_CLOSING: // As long as we are in closing state, we call closeConnection() again and wait for it to finish or timeout
518+
closeConnection();
519+
break;
492520
case STATE_WEBSOCKET: // Do handling of the websocket
493521

494522
break;

https/HTTPSConnection.hpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,20 @@ class HTTPSConnection : private ConnectionContext {
9898
// Timestamp of the last transmission action
9999
unsigned long _lastTransmissionTS;
100100

101+
// Timestamp of when the shutdown was started
102+
unsigned long _shutdownTS;
103+
101104
// Internal state machine of the connection:
102105
//
103106
// O --- > STATE_UNDEFINED -- initialize() --> STATE_INITIAL -- get / http/1.1 --> STATE_REQUEST_FINISHED --.
104107
// | | | |
105108
// | | | | Host: ...\r\n
106109
// STATE_ERROR <- on error-----------------------<---------------------------------------< | Foo: bar\r\n
107-
// | | | \r\n
108-
// close() | | | \r\n
109-
// STATE_CLOSED <----- STATE_WEBSOCKET <-. | | |
110+
// ^ | | | \r\n
111+
// | shutdown .--> STATE_CLOSED | | | \r\n
112+
// | fails | | | |
113+
// | | close() | | |
114+
// STATE_CLOSING <---- STATE_WEBSOCKET <-. | | |
110115
// ^ | | | |
111116
// `---------- close() ---------- STATE_BODY_FINISHED <-- Body received or GET -- STATE_HEADERS_FINISHED <-´
112117
//
@@ -125,6 +130,8 @@ class HTTPSConnection : private ConnectionContext {
125130
STATE_BODY_FINISHED,
126131
// The connection is in websocket mode
127132
STATE_WEBSOCKET,
133+
// The connection is about to close (and waiting for the client to send close notify)
134+
STATE_CLOSING,
128135
// The connection has been closed
129136
STATE_CLOSED,
130137
// An error has occured

https/HTTPSServer.cpp

+16-4
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,23 @@ void HTTPSServer::stop() {
8383
_running = false;
8484

8585
// Clean up the connections
86-
for(int i = 0; i < _maxConnections; i++) {
87-
if (_connections[i] != NULL) {
88-
_connections[i]->closeConnection();
89-
delete _connections[i];
86+
bool hasOpenConnections = true;
87+
while(hasOpenConnections) {
88+
hasOpenConnections = false;
89+
for(int i = 0; i < _maxConnections; i++) {
90+
if (_connections[i] != NULL) {
91+
_connections[i]->closeConnection();
92+
93+
// Check if closing succeeded. If not, we need to call the close function multiple times
94+
// and wait for the client
95+
if (_connections[i]->isClosed()) {
96+
delete _connections[i];
97+
} else {
98+
hasOpenConnections = true;
99+
}
100+
}
90101
}
102+
delay(1);
91103
}
92104

93105
// Close the actual server socket

https/HTTPSServerConstants.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@
3535
// Timeout for an HTTPS connection without any transmission
3636
#define HTTPS_CONNECTION_TIMEOUT 20000
3737

38+
// Timeout used to wait for shutdown of SSL connection (ms)
39+
// (time for the client to return notify close flag) - without it, truncation attacks might be possible
40+
#define HTTPS_SHUTDOWN_TIMEOUT 5000
41+
3842
#endif /* HTTPS_HTTPSSERVERCONSTANTS_HPP_ */

0 commit comments

Comments
 (0)