Skip to content
This repository was archived by the owner on Jan 20, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a251e59
- very early version with mbed TLS support.
fremouw Sep 26, 2018
d4257e6
improved TLS support, removed some not needed debug statements.
fremouw Oct 25, 2018
eeff609
removed some more logging.
fremouw Oct 25, 2018
f2127a0
getting there, seems to start working, there are still some issues wi…
fremouw Nov 5, 2018
3c9fbd9
add fix for reading larger amounts of data.
fremouw Nov 20, 2018
d501a90
some code clean-up
fremouw Nov 29, 2018
c9dd608
more code clean-up.
fremouw Nov 29, 2018
52e550b
revert IDF changes, so it works with the latest stable IDF.
fremouw Jan 9, 2019
001ae06
Not a fan of defines, but to keep things in line with the esp8266 ver…
fremouw Jan 10, 2019
9f7a918
clean-up
fremouw Jan 11, 2019
5187673
merge origin/idf-update into mbed-tls, as it's now part of the latest…
fremouw Jan 11, 2019
373c334
Merge branch 'master' into mbed-tls
fremouw Jan 15, 2019
d631d7d
add some dummy functions so we can compile when the ESP Async WebServ…
fremouw Jan 15, 2019
78f952e
allow setting a root CA.
fremouw Jan 17, 2019
0879ba2
oops, set debug disabled as default again.
fremouw Jan 17, 2019
ef50357
add ASYNC_TCP_SSL_ENABLED around setRootCa call.
fremouw Jan 18, 2019
3fa9abf
Update README.md
fremouw Mar 2, 2019
5e06389
add support for pre-shared key TLS cipher suites
tve Apr 16, 2019
5e511de
update mbed-tls implementation to account for restructing
tve Jun 25, 2019
8e55be4
don't assign _hostname unless necessary
tve Jun 25, 2019
9cba86e
fix indentation; make SSL timeout a #define
tve Jul 26, 2019
f34df7f
make SSL timeout a #define, 2nd try
tve Jul 26, 2019
7bca0f4
Merge remote-tracking branch 'upstream/master' into mbed-tls-try2
tve Jul 26, 2019
8ef604b
fix while loop in _recv; fix calls into LwIP; add some comments
tve Aug 9, 2019
b7ee312
fix c/c++ linking from tcp_ssl to tcp code
tve Aug 9, 2019
73480f3
TLS fixes: pbuf free bug when reading; add debug printfs; useless ext…
tve Aug 9, 2019
8b52f6c
add minor comment
tve Aug 9, 2019
1e8da66
merge upstream into mbed-tls
tve Sep 24, 2019
8e77fe7
fix Codacy issues
tve Sep 25, 2019
b0b2bac
Merge remote-tracking branch 'upstream/master' into mbed-tls-try2
tve Sep 25, 2019
3cc7048
fix issues with closed_slots
tve Sep 26, 2019
abdd496
Merge branch 'master' into mbed-tls-try2
me-no-dev Oct 14, 2019
44b3d6b
mbed-tls-try2 updates (#3)
robert-alfaro Jan 22, 2020
540bf7d
fix codacity strlen issues
tve Jan 22, 2020
3d67ffe
Merge branch 'master' into mbed-tls-try2
tve Jan 22, 2020
59f83d8
add client certificate authentication
r3no1t Jan 2, 2020
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ This library is the base for [ESPAsyncWebServer](https://github.com/me-no-dev/ES

## AsyncClient and AsyncServer
The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use.

## TLS support
Support for TLS is added using mbed TLS, for now only the client part is supported. You can enable this by adding the flag ASYNC_TCP_SSL_ENABLED to your build flags (-DASYNC_TCP_SSL_ENABLED). If you'd like to set a root certificate you can use the setRootCa function on AsyncClient. Feel free to add support for the server side as well :-)

In addition to the regular certificate based cipher suites there is also support for Pre-Shared Key
cipher suites. Use `setPsk` to define the PSK identifier and PSK itself. The PSK needs to be
provided in the form of a hex string (and easy way to generate a PSK is to use md5sum).
161 changes: 161 additions & 0 deletions src/AsyncTCP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,14 @@ AsyncClient::AsyncClient(tcp_pcb* pcb)
, _rx_since_timeout(0)
, _ack_timeout(ASYNC_MAX_ACK_TIME)
, _connect_port(0)
#if ASYNC_TCP_SSL_ENABLED
, _root_ca_len(0)
, _root_ca(NULL)
, _pcb_secure(false)
, _handshake_done(true)
, _psk_ident(0)
, _psk(0)
#endif // ASYNC_TCP_SSL_ENABLED<Paste>
, prev(NULL)
, next(NULL)
{
Expand Down Expand Up @@ -571,6 +579,19 @@ AsyncClient& AsyncClient::operator=(const AsyncClient& other){
tcp_sent(_pcb, &_tcp_sent);
tcp_err(_pcb, &_tcp_error);
tcp_poll(_pcb, &_tcp_poll, 1);
#if ASYNC_TCP_SSL_ENABLED
if(tcp_ssl_has(_pcb)){
_pcb_secure = true;
_handshake_done = false;
tcp_ssl_arg(_pcb, this);
tcp_ssl_data(_pcb, &_s_data);
tcp_ssl_handshake(_pcb, &_s_handshake);
tcp_ssl_err(_pcb, &_s_ssl_error);
} else {
_pcb_secure = false;
_handshake_done = true;
}
#endif // ASYNC_TCP_SSL_ENABLED
}
return *this;
}
Expand Down Expand Up @@ -642,7 +663,11 @@ void AsyncClient::onPoll(AcConnectHandler cb, void* arg){
* Main Public Methods
* */

#if ASYNC_TCP_SSL_ENABLED
bool AsyncClient::connect(IPAddress ip, uint16_t port, bool secure){
#else
bool AsyncClient::connect(IPAddress ip, uint16_t port){
#endif // ASYNC_TCP_SSL_ENABLED
if (_pcb){
log_w("already connected, state %d", _pcb->state);
return false;
Expand All @@ -662,6 +687,11 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){
return false;
}

#if ASYNC_TCP_SSL_ENABLED
_pcb_secure = secure;
_handshake_done = !secure;
#endif // ASYNC_TCP_SSL_ENABLED

tcp_arg(pcb, this);
tcp_err(pcb, &_tcp_error);
tcp_recv(pcb, &_tcp_recv);
Expand All @@ -672,13 +702,27 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){
return true;
}

#if ASYNC_TCP_SSL_ENABLED
bool AsyncClient::connect(const char* host, uint16_t port, bool secure){
#else
bool AsyncClient::connect(const char* host, uint16_t port){
#endif // ASYNC_TCP_SSL_ENABLED
ip_addr_t addr;
err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this);
if(err == ERR_OK) {
#if ASYNC_TCP_SSL_ENABLED
_hostname = host;
return connect(IPAddress(addr.u_addr.ip4.addr), port, secure);
#else
return connect(IPAddress(addr.u_addr.ip4.addr), port);
#endif // ASYNC_TCP_SSL_ENABLED
} else if(err == ERR_INPROGRESS) {
_connect_port = port;
#if ASYNC_TCP_SSL_ENABLED
_hostname = host;
_pcb_secure = secure;
_handshake_done = !secure;
#endif // ASYNC_TCP_SSL_ENABLED
return true;
}
log_e("error: %d", err);
Expand All @@ -700,6 +744,18 @@ int8_t AsyncClient::abort(){
return ERR_ABRT;
}

#if ASYNC_TCP_SSL_ENABLED
void AsyncClient::setRootCa(const char* rootca, const size_t len) {
_root_ca = (char*)rootca;
_root_ca_len = len;
}

void AsyncClient::setPsk(const char* psk_ident, const char* psk) {
_psk_ident = psk_ident;
_psk = psk;
}
#endif // ASYNC_TCP_SSL_ENABLED

size_t AsyncClient::space(){
if((_pcb != NULL) && (_pcb->state == 4)){
return tcp_sndbuf(_pcb);
Expand All @@ -715,6 +771,19 @@ size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) {
if(!room) {
return 0;
}
#if ASYNC_TCP_SSL_ENABLED
if(_pcb_secure){
int sent = tcp_ssl_write(_pcb, (uint8_t*)data, size);
if(sent >= 0){
// @ToDo: ???
//_tx_unacked_len += sent;
return sent;
}
//log_i("add: tcp_ssl_write: %d", sent);
_close();
return 0;
}
#endif // ASYNC_TCP_SSL_ENABLED
size_t will_send = (room < size) ? room : size;
int8_t err = ERR_OK;
err = _tcp_write(_pcb, data, will_send, apiflags, this);
Expand All @@ -725,6 +794,9 @@ size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) {
}

bool AsyncClient::send(){
#if ASYNC_TCP_SSL_ENABLED
if(_pcb_secure) return true;
#endif // ASYNC_TCP_SSL_ENABLED
int8_t err = ERR_OK;
err = _tcp_output(_pcb, this);
if(err == ERR_OK){
Expand Down Expand Up @@ -762,6 +834,11 @@ int8_t AsyncClient::_close(){
int8_t err = ERR_OK;
if(_pcb) {
//log_i("");
#if ASYNC_TCP_SSL_ENABLED
if(_pcb_secure){
tcp_ssl_free(_pcb);
}
#endif // ASYNC_TCP_SSL_ENABLED
tcp_arg(_pcb, NULL);
tcp_sent(_pcb, NULL);
tcp_recv(_pcb, NULL);
Expand Down Expand Up @@ -792,15 +869,47 @@ int8_t AsyncClient::_connected(void* pcb, int8_t err){
// tcp_recv(_pcb, &_tcp_recv);
// tcp_sent(_pcb, &_tcp_sent);
// tcp_poll(_pcb, &_tcp_poll, 1);
#if ASYNC_TCP_SSL_ENABLED
if(_pcb_secure){
bool err = false;
if(_root_ca) {
err = tcp_ssl_new_client(_pcb, _hostname.empty() ? NULL : _hostname.c_str(),
_root_ca, _root_ca_len) < 0;
} else {
err = tcp_ssl_new_psk_client(_pcb, _psk_ident, _psk) < 0;
}
if (err) {
log_e("closing....");
return _close();
}

tcp_ssl_arg(_pcb, this);
tcp_ssl_data(_pcb, &_s_data);
tcp_ssl_handshake(_pcb, &_s_handshake);
tcp_ssl_err(_pcb, &_s_ssl_error);
}
#endif // ASYNC_TCP_SSL_ENABLED
}
#if ASYNC_TCP_SSL_ENABLED
// _connect_cb happens after SSL handshake if this is a secure connection
if(_connect_cb && !_pcb_secure) {
_connect_cb(_connect_cb_arg, this);
}
#else
if(_connect_cb) {
_connect_cb(_connect_cb_arg, this);
}
#endif // ASYNC_TCP_SSL_ENABLED
return ERR_OK;
}

void AsyncClient::_error(int8_t err) {
if(_pcb){
#if ASYNC_TCP_SSL_ENABLED
if(_pcb_secure){
tcp_ssl_free(_pcb);
}
#endif // ASYNC_TCP_SSL_ENABLED
tcp_arg(_pcb, NULL);
tcp_sent(_pcb, NULL);
tcp_recv(_pcb, NULL);
Expand All @@ -816,6 +925,14 @@ void AsyncClient::_error(int8_t err) {
}
}

#if ASYNC_TCP_SSL_ENABLED
void AsyncClient::_ssl_error(int8_t err){
if(_error_cb) {
_error_cb(_error_cb_arg, this, err+64);
}
}
#endif // ASYNC_TCP_SSL_ENABLED

//In LwIP Thread
int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) {
if(!_pcb || pcb != _pcb){
Expand Down Expand Up @@ -856,6 +973,22 @@ int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) {
int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) {
while(pb != NULL) {
_rx_last_packet = millis();

#if ASYNC_TCP_SSL_ENABLED
if(_pcb_secure){
// log_i("_recv: %d\n", pb->tot_len);
int read_bytes = tcp_ssl_read(pcb, pb);
if(read_bytes < 0){
if (read_bytes != MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
log_e("_recv err: %d\n", read_bytes);
_close();
}
return ERR_BUF; // for lack of a better error value
}
return ERR_OK;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are in a while loop. should continue here maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}
#endif // ASYNC_TCP_SSL_ENABLED

//we should not ack before we assimilate the data
_ack_pcb = true;
pbuf *b = pb;
Expand Down Expand Up @@ -904,6 +1037,13 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){
_close();
return ERR_OK;
}
#if ASYNC_TCP_SSL_ENABLED
if(_pcb_secure && !_handshake_done && (now - _rx_last_packet) >= 5000){
log_w("ssl handshake timeout %d", pcb->state);
_close();
return ERR_OK;
}
#endif // ASYNC_TCP_SSL_ENABLED
// Everything is fine
if(_poll_cb) {
_poll_cb(_poll_cb_arg, this);
Expand All @@ -913,7 +1053,11 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){

void AsyncClient::_dns_found(struct ip_addr *ipaddr){
if(ipaddr){
#if ASYNC_TCP_SSL_ENABLED
connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port, _pcb_secure);
#else
connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port);
#endif // ASYNC_TCP_SSL_ENABLED
} else {
if(_error_cb) {
_error_cb(_error_cb_arg, this, -55);
Expand Down Expand Up @@ -1164,6 +1308,23 @@ int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){
return reinterpret_cast<AsyncClient*>(arg)->_connected(pcb, err);
}

#if ASYNC_TCP_SSL_ENABLED
void AsyncClient::_s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len){
AsyncClient *c = reinterpret_cast<AsyncClient*>(arg);
if(c->_recv_cb) c->_recv_cb(c->_recv_cb_arg, c, data, len);
}

void AsyncClient::_s_handshake(void *arg, struct tcp_pcb *tcp, struct tcp_ssl_pcb* ssl){
AsyncClient *c = reinterpret_cast<AsyncClient*>(arg);
c->_handshake_done = true;
if(c->_connect_cb) c->_connect_cb(c->_connect_cb_arg, c);
}

void AsyncClient::_s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err){
reinterpret_cast<AsyncClient*>(arg)->_ssl_error(err);
}
#endif // ASYNC_TCP_SSL_ENABLED

/*
Async TCP Server
*/
Expand Down
38 changes: 38 additions & 0 deletions src/AsyncTCP.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#include "IPAddress.h"
#include "sdkconfig.h"
#include <functional>
#include <string>
#include <ssl_client.h>
#include "tcp_mbedtls.h"
extern "C" {
#include "freertos/semphr.h"
#include "lwip/pbuf.h"
Expand Down Expand Up @@ -65,8 +68,16 @@ class AsyncClient {
bool operator!=(const AsyncClient &other) {
return !(*this == other);
}

#if ASYNC_TCP_SSL_ENABLED
bool connect(IPAddress ip, uint16_t port, bool secure = false);
bool connect(const char* host, uint16_t port, bool secure = false);
void setRootCa(const char* rootca, const size_t len);
void setPsk(const char* psk_ident, const char* psk);
#else
bool connect(IPAddress ip, uint16_t port);
bool connect(const char* host, uint16_t port);
#endif // ASYNC_TCP_SSL_ENABLED
void close(bool now = false);
void stop();
int8_t abort();
Expand Down Expand Up @@ -135,12 +146,18 @@ class AsyncClient {
static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len);
static int8_t _s_connected(void* arg, void* tpcb, int8_t err);
static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg);
#if ASYNC_TCP_SSL_ENABLED
static void _s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len);
static void _s_handshake(void *arg, struct tcp_pcb *tcp, struct tcp_ssl_pcb* ssl);
static void _s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err);
#endif // ASYNC_TCP_SSL_ENABLED

int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err);
tcp_pcb * pcb(){ return _pcb; }

protected:
tcp_pcb* _pcb;
std::string _hostname;

AcConnectHandler _connect_cb;
void* _connect_cb_arg;
Expand Down Expand Up @@ -168,6 +185,15 @@ class AsyncClient {
uint32_t _ack_timeout;
uint16_t _connect_port;

#if ASYNC_TCP_SSL_ENABLED
size_t _root_ca_len;
char* _root_ca;
bool _pcb_secure;
bool _handshake_done;
const char* _psk_ident;
const char* _psk;
#endif // ASYNC_TCP_SSL_ENABLED

int8_t _close();
int8_t _connected(void* pcb, int8_t err);
void _error(int8_t err);
Expand All @@ -176,18 +202,30 @@ class AsyncClient {
int8_t _fin(tcp_pcb* pcb, int8_t err);
int8_t _lwip_fin(tcp_pcb* pcb, int8_t err);
void _dns_found(struct ip_addr *ipaddr);
#if ASYNC_TCP_SSL_ENABLED
void _ssl_error(int8_t err);
#endif // ASYNC_TCP_SSL_ENABLED

public:
AsyncClient* prev;
AsyncClient* next;
};

#if ASYNC_TCP_SSL_ENABLED
typedef std::function<int(void* arg, const char *filename, uint8_t **buf)> AcSSlFileHandler;
#endif

class AsyncServer {
public:
AsyncServer(IPAddress addr, uint16_t port);
AsyncServer(uint16_t port);
~AsyncServer();
void onClient(AcConnectHandler cb, void* arg);
#if ASYNC_TCP_SSL_ENABLED
// Dummy, so it compiles with ESP Async WebServer library enabled.
void onSslFileRequest(AcSSlFileHandler cb, void* arg) {};
void beginSecure(const char *cert, const char *private_key_file, const char *password) {};
#endif
void begin();
void end();
void setNoDelay(bool nodelay);
Expand Down
Loading