/
singlwolf
/
Radiola-2S3
Обзор
Документация
Войти
/
singlwolf
/
Radiola-2S3
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/AsyncClient_SSL/src/AsyncSSLClient.cpp
2 174 строки
43 KB
SinglWolf
v1.0.6
15 апр 2026, 05:04
15 апр 2026, 05:04
55b96c5
Код
Авторство
О чём код?
#include "AsyncSSLClient.h" #include "esp32-hal.h" #include <utility> #if CONFIG_ACLIENT_DBG #include "HardwareSerial.h" #include "esp_log_color.h" #define txtPrintInfo(fmt, ...) \ do \ { \ Serial.printf(LOG_COLOR_I); \ Serial.printf("[CLI INFO] "); \ Serial.printf(fmt, ##__VA_ARGS__); \ Serial.printf(LOG_RESET_COLOR "\n"); \ } while (0) // #define txtPrintWarn(fmt, ...) \ do \ { \ Serial.printf(LOG_COLOR_W); \ Serial.printf("[CLI WARN] "); \ Serial.printf(fmt, ##__VA_ARGS__); \ Serial.printf(LOG_RESET_COLOR "\n"); \ } while (0) // #define txtPrintDebug(fmt, ...) \ do \ { \ Serial.printf(LOG_ANSI_COLOR(LOG_COLOR_BLUE)); \ Serial.printf("[CLI DEBUG] "); \ Serial.printf(fmt, ##__VA_ARGS__); \ Serial.printf(LOG_RESET_COLOR "\n"); \ } while (0) // #define txtPrintError(fmt, ...) \ do \ { \ Serial.printf(LOG_COLOR_E); \ Serial.printf("[CLI ERROR] "); \ Serial.printf(fmt, ##__VA_ARGS__); \ Serial.printf(LOG_RESET_COLOR "\n"); \ } while (0) #else #define txtPrintInfo(fmt, ...) \ do \ { \ } while (0) #define txtPrintWarn(fmt, ...) \ do \ { \ } while (0) #define txtPrintDebug(fmt, ...) \ do \ { \ } while (0) #define txtPrintError(fmt, ...) \ do \ { \ } while (0) #endif extern "C" { #include "lwip/tcp.h" #include "lwip/dns.h" #include "lwip/err.h" } #if CONFIG_ASYNC_SSL_CLIENT_USE_WDT #include "esp_task_wdt.h" #endif AsyncSSLClient asyncClient; ////////////////////////////////////////////////////////////////////////////////////////// using client_error_t = enum : int8_t { ERR_WRITE = -17, ERR_READ = -18, ERR_HANDSHAKE = -19, ERR_DNS = -20, ERR_ASK_TIMEOUT = -21, ERR_RX_TIMEOUT = -22, ERR_HANDSHAKE_TIMEOUT = -23, }; using lwip_event_t = enum : uint8_t { LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_FIN, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR, LWIP_TCP_CONNECTED, LWIP_TCP_DNS }; using lwip_event_packet_t = struct { lwip_event_t event; union { struct { void *pcb; int8_t err; } connected; struct { int8_t err; } error; struct { tcp_pcb *pcb; uint16_t len; } sent; struct { tcp_pcb *pcb; pbuf *pb; int8_t err; } recv; struct { tcp_pcb *pcb; int8_t err; } fin; struct { tcp_pcb *pcb; } poll; struct { const char *name; ip_addr_t addr; } dns; }; }; ///////////////////////////////////////////////// // Detail class for interacting with AsyncClient internals, but without exposing the API class AsyncSSLTCP_detail { public: // Helper functions static void __attribute__((visibility("internal"))) _handle_async_event(lwip_event_packet_t *event); // LwIP TCP event callbacks that (will) require privileged access static int8_t __attribute__((visibility("internal"))) _tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, int8_t err); static int8_t __attribute__((visibility("internal"))) _tcp_sent(void *arg, struct tcp_pcb *pcb, uint16_t len); static void __attribute__((visibility("internal"))) _tcp_error(void *arg, int8_t err); static int8_t __attribute__((visibility("internal"))) _tcp_poll(void *arg, struct tcp_pcb *pcb); }; ///////////////////////////////////////////////// static QueueHandle_t async_queue; static TaskHandle_t async_service_task_handle = nullptr; ///////////////////////////////////////////// // tcp_mbedtls ///////////////////////////////////////////// #include "mbedtls/ssl.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/error.h" struct tcp_ssl_pcb { tcp_pcb *tcp; mbedtls_ssl_context *ssl_ctx; mbedtls_ssl_config *ssl_conf; mbedtls_ctr_drbg_context *drbg_ctx; mbedtls_entropy_context *entropy_ctx; pbuf *tcp_pbuf; size_t pbuf_offset; }; using tcp_ssl_t = tcp_ssl_pcb; static tcp_ssl_t tcp_ssl_client = {}; // static tcp_ssl_t *tcp_ssl_array = nullptr; static const char pers[] = "esp32-tls"; static int handle_error(int err) { #ifdef MBEDTLS_ERROR_C char error_buf[100]; mbedtls_strerror(err, error_buf, 100); printf(LOG_COLOR_E); printf("MBEDTLS: %s", error_buf); printf(LOG_RESET_COLOR "\n"); #endif return err; } ///////////////////////////////////////////// void tcp_ssl_free(); int tcp_ssl_send(void *ctx, const unsigned char *buf, size_t len); int tcp_ssl_recv(void *ctx, unsigned char *buf, size_t len); int tcp_ssl_new_client(tcp_pcb *tcp, const char *hostname); ///////////////////////////////////////////// void tcp_ssl_free() { txtPrintWarn("tcp_ssl_free()"); if (tcp_ssl_client.tcp_pbuf != nullptr) { struct pbuf *p = tcp_ssl_client.tcp_pbuf; tcp_ssl_client.tcp_pbuf = nullptr; pbuf_free(p); } if (tcp_ssl_client.ssl_ctx != nullptr) { mbedtls_ssl_free(tcp_ssl_client.ssl_ctx); tcp_ssl_client.ssl_ctx = nullptr; } if (tcp_ssl_client.ssl_conf != nullptr) { mbedtls_ssl_config_free(tcp_ssl_client.ssl_conf); tcp_ssl_client.ssl_conf = nullptr; } if (tcp_ssl_client.drbg_ctx != nullptr) { mbedtls_ctr_drbg_free(tcp_ssl_client.drbg_ctx); tcp_ssl_client.drbg_ctx = nullptr; } if (tcp_ssl_client.entropy_ctx != nullptr) { mbedtls_entropy_free(tcp_ssl_client.entropy_ctx); tcp_ssl_client.entropy_ctx = nullptr; } } // int tcp_ssl_new_client(struct tcp_pcb *tcp, const char *hostname) { if (tcp == nullptr) { return -127; } int ret = ERR_OK; tcp_ssl_client.tcp = tcp; tcp_ssl_client.tcp_pbuf = nullptr; tcp_ssl_client.pbuf_offset = 0; if (tcp_ssl_client.ssl_ctx == nullptr) { tcp_ssl_client.ssl_ctx = (mbedtls_ssl_context *)malloc(sizeof(mbedtls_ssl_context)); tcp_ssl_client.ssl_conf = (mbedtls_ssl_config *)malloc(sizeof(mbedtls_ssl_config)); tcp_ssl_client.drbg_ctx = (mbedtls_ctr_drbg_context *)malloc(sizeof(mbedtls_ctr_drbg_context)); tcp_ssl_client.entropy_ctx = (mbedtls_entropy_context *)malloc(sizeof(mbedtls_entropy_context)); } mbedtls_entropy_init(tcp_ssl_client.entropy_ctx); mbedtls_ctr_drbg_init(tcp_ssl_client.drbg_ctx); mbedtls_ssl_init(tcp_ssl_client.ssl_ctx); mbedtls_ssl_config_init(tcp_ssl_client.ssl_conf); mbedtls_ctr_drbg_seed(tcp_ssl_client.drbg_ctx, mbedtls_entropy_func, tcp_ssl_client.entropy_ctx, (const unsigned char *)pers, sizeof(pers)); ret = mbedtls_ssl_config_defaults(tcp_ssl_client.ssl_conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret != ERR_OK) { txtPrintError("error setting SSL config."); return ret; } mbedtls_ssl_conf_authmode(tcp_ssl_client.ssl_conf, MBEDTLS_SSL_VERIFY_NONE); if (hostname != nullptr) { txtPrintWarn("setting the hostname: %s", hostname); ret = mbedtls_ssl_set_hostname(tcp_ssl_client.ssl_ctx, hostname); if (ret != ERR_OK) { return ret; } } mbedtls_ssl_conf_rng(tcp_ssl_client.ssl_conf, mbedtls_ctr_drbg_random, tcp_ssl_client.drbg_ctx); ret = mbedtls_ssl_setup(tcp_ssl_client.ssl_ctx, tcp_ssl_client.ssl_conf); if (ret != ERR_OK) { return ret; } mbedtls_ssl_set_bio(tcp_ssl_client.ssl_ctx, (void *)&tcp_ssl_client, tcp_ssl_send, tcp_ssl_recv, nullptr); // Start handshake. ret = mbedtls_ssl_handshake(tcp_ssl_client.ssl_ctx); if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = ERR_OK; } else { txtPrintError("handshake error!"); } return ret; } ///////////////////////////////////////////// static inline bool _init_async_event_queue() { if (!async_queue) { async_queue = xQueueCreate(ASYNC_SSL_QUEUE_LENGTH, sizeof(lwip_event_packet_t *)); if (!async_queue) { return false; } } return true; } ///////////////////////////////////////////// static inline bool _send_async_event(lwip_event_packet_t **e) { return async_queue && xQueueSend(async_queue, static_cast<const void *>(e), portMAX_DELAY) == pdPASS; } ///////////////////////////////////////////// static inline bool _prepend_async_event(lwip_event_packet_t **e) { return async_queue && xQueueSendToFront(async_queue, static_cast<const void *>(e), portMAX_DELAY) == pdPASS; } ///////////////////////////////////////////// static inline bool _get_async_event(lwip_event_packet_t **e) { return async_queue && xQueueReceive(async_queue, static_cast<void *>(e), portMAX_DELAY) == pdPASS; } ///////////////////////////////////////////// static bool _remove_events() { if (!async_queue) return false; lwip_event_packet_t *packet = nullptr; while (xQueueReceive(async_queue, (void *)&packet, 0) == pdPASS) { if (packet) { txtPrintInfo("remove_event: %d", packet->event); if ((packet->event == LWIP_TCP_RECV) && (packet->recv.pb != nullptr)) { txtPrintInfo("pbuf_free(packet->recv_pb): LWIP_TCP_RECV"); pbuf_free(packet->recv.pb); } free(packet); } } tcp_ssl_free(); return true; } ///////////////////////////////////////////// void AsyncSSLTCP_detail::_handle_async_event(lwip_event_packet_t *e) { if (e->event == LWIP_TCP_CLEAR) { txtPrintInfo("_event: LWIP_TCP_CLEAR"); _remove_events(); } else if (e->event == LWIP_TCP_RECV) { txtPrintInfo("_event: LWIP_TCP_RECV = %p", e->recv.pcb); if (asyncClient._pcb_secure) asyncClient._recv_ssl(e->recv.pcb, e->recv.pb, e->recv.err); else asyncClient._recv(e->recv.pcb, e->recv.pb, e->recv.err); e->recv.pb = nullptr; // given to client } else if (e->event == LWIP_TCP_FIN) { txtPrintInfo("_event: LWIP_TCP_FIN = %p", e->fin.pcb); asyncClient._fin(e->fin.pcb, e->fin.err); } else if (e->event == LWIP_TCP_SENT) { txtPrintInfo("_event: LWIP_TCP_SENT = %p", e->sent.pcb); asyncClient._sent(e->sent.pcb, e->sent.len); } else if (e->event == LWIP_TCP_POLL) { txtPrintInfo("_event: LWIP_TCP_POLL = %p", e->poll.pcb); asyncClient._poll(e->poll.pcb); } else if (e->event == LWIP_TCP_ERROR) { txtPrintInfo("_event: LWIP_TCP_ERROR = %d", e->error.err); asyncClient._error(e->error.err); } else if (e->event == LWIP_TCP_CONNECTED) { txtPrintInfo("_event: connected.pcb = %p", e->connected.pcb); txtPrintInfo("_event: LWIP_TCP_CONNECTED = %d", e->connected.err); asyncClient._connected(e->connected.pcb, e->connected.err); } else if (e->event == LWIP_TCP_DNS) { txtPrintInfo("_event: LWIP_TCP_DNS, name = %s, IP = %s", e->dns.name, ipaddr_ntoa(&e->dns.addr)); asyncClient._dns_found(&e->dns.addr); } free((void *)(e)); } ///////////////////////////////////////////// static void _async_service_task(void *pvParameters) { lwip_event_packet_t *packet = nullptr; for (;;) { if (_get_async_event(&packet)) { #if CONFIG_ASYNC_SSL_CLIENT_USE_WDT if (esp_task_wdt_add(NULL) != ESP_OK) { txtPrintError("Failed to add async task to WDT"); } #endif if (packet) AsyncSSLTCP_detail::_handle_async_event(packet); else { txtPrintError("_async_service_task, NUL packet"); } #if CONFIG_ASYNC_SSL_CLIENT_USE_WDT if (esp_task_wdt_delete(NULL) != ESP_OK) { txtPrintError("Failed to remove loop task from WDT"); } #endif } } vTaskDelete(nullptr); async_service_task_handle = nullptr; } ///////////////////////////////////////////// static bool _start_async_task() { if (!_init_async_event_queue()) { return false; } if (!async_service_task_handle) { xTaskCreateUniversal(_async_service_task, "async_ssl_client", CONFIG_ASYNC_SSL_CLIENT_STACK, nullptr, CONFIG_ASYNC_SSL_CLIENT_PRIORITY, &async_service_task_handle, CONFIG_ASYNC_SSL_CLIENT_RUNNING_CORE); if (!async_service_task_handle) { return false; } } return true; } //////////////////// /* LwIP Callbacks */ //////////////////// static int8_t _tcp_clear_events() { auto *e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_CLEAR; if (!_prepend_async_event(&e)) { free((void *)(e)); } return ERR_OK; } ///////////////////////////////////////////// static void _reset_tcp_callbacks(tcp_pcb *pcb) { tcp_arg(pcb, NULL); tcp_sent(pcb, NULL); tcp_recv(pcb, NULL); tcp_err(pcb, NULL); tcp_poll(pcb, NULL, 0); _tcp_clear_events(); } ///////////////////////////////////////////// static int8_t _tcp_connected(void *arg, tcp_pcb *pcb, int8_t err) { txtPrintDebug("_tcp_connected: pcb = %p", pcb); auto *e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_CONNECTED; e->connected.pcb = pcb; e->connected.err = err; if (!_prepend_async_event(&e)) { free((void *)(e)); } return ERR_OK; } ///////////////////////////////////////////// int8_t AsyncSSLTCP_detail::_tcp_poll(void *arg, struct tcp_pcb *pcb) { txtPrintDebug("_tcp_poll: pcb = %p", pcb); auto *e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_POLL; e->poll.pcb = pcb; if (!_send_async_event(&e)) { free((void *)(e)); } return ERR_OK; } ///////////////////////////////////////////// int8_t AsyncSSLTCP_detail::_tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, int8_t err) { auto *e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); if (pb) { txtPrintDebug("_tcp_recv: pcb = %p", pcb); e->event = LWIP_TCP_RECV; e->recv.pcb = pcb; e->recv.pb = pb; e->recv.err = err; } else { txtPrintDebug("_tcp_recv: failed, pcb = %p", pcb); e->event = LWIP_TCP_FIN; e->fin.pcb = pcb; e->fin.err = err; // close the PCB in LwIP thread asyncClient._lwip_fin(e->fin.pcb, e->fin.err); } if (!_send_async_event(&e)) { free((void *)(e)); } return ERR_OK; } ///////////////////////////////////////////// int8_t AsyncSSLTCP_detail::_tcp_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { txtPrintDebug("_tcp_sent: pcb = %p", pcb); auto *e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_SENT; e->sent.pcb = pcb; e->sent.len = len; if (!_send_async_event(&e)) { free((void *)(e)); } return ERR_OK; } ///////////////////////////////////////////// void AsyncSSLTCP_detail::_tcp_error(void *arg, int8_t err) { txtPrintDebug("_tcp_error: arg = %p", arg); auto *e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); e->event = LWIP_TCP_ERROR; e->error.err = err; if (!_send_async_event(&e)) { free((void *)(e)); } } ///////////////////////////////////////////// static void _tcp_dns_found(const char *name, struct ip_addr *ipaddr, void *arg) { auto *e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); txtPrintDebug("_tcp_dns_found: name = %s, IP = %s", name, ipaddr_ntoa(ipaddr)); txtPrintDebug("_tcp_dns_found: arg = %p", arg); e->event = LWIP_TCP_DNS; e->dns.name = name; if (ipaddr) { memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr)); } else { memset(&e->dns.addr, 0, sizeof(e->dns.addr)); } if (!_send_async_event(&e)) { free((void *)(e)); } } ////////////////////// /* TCP/IP API Calls */ ////////////////////// #include "lwip/priv/tcpip_priv.h" using tcp_api_call_t = struct { struct tcpip_api_call_data call; tcp_pcb **pcb; int8_t err; union { struct { const char *data; size_t size; uint8_t apiflags; } write; size_t received; struct { ip_addr_t *addr; uint16_t port; tcp_connected_fn cb; } connect; }; }; ///////////////////////////////////////////// static err_t tcp_output_api(struct tcpip_api_call_data *api_call_msg) { auto *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (*msg->pcb) { msg->err = tcp_output(*msg->pcb); } return msg->err; } ///////////////////////////////////////////// static esp_err_t _tcp_output(tcp_pcb **pcb) { if (!pcb || !*pcb) { return ERR_CONN; } tcp_api_call_t msg; msg.pcb = pcb; tcpip_api_call(tcp_output_api, (struct tcpip_api_call_data *)&msg); return msg.err; } ///////////////////////////////////////////// static err_t _tcp_write_api(struct tcpip_api_call_data *api_call_msg) { auto *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (*msg->pcb) { msg->err = tcp_write(*msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); } return msg->err; } ///////////////////////////////////////////// static esp_err_t _tcp_write(tcp_pcb **pcb, const char *data, size_t size, uint8_t apiflags) { if (!pcb || !*pcb) { return ERR_CONN; } tcp_api_call_t msg; msg.pcb = pcb; msg.write.data = data; msg.write.size = size; msg.write.apiflags = apiflags; tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data *)&msg); return msg.err; } ///////////////////////////////////////////// static err_t _tcp_recved_api(struct tcpip_api_call_data *api_call_msg) { tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (asyncClient.pcb() == *msg->pcb) { msg->err = 0; tcp_recved(*msg->pcb, msg->received); } return msg->err; } ///////////////////////////////////////////// static esp_err_t _tcp_recved(tcp_pcb **pcb, size_t len) { if (!pcb || !*pcb) { return ERR_CONN; } tcp_api_call_t msg; msg.pcb = pcb; msg.received = len; tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data *)&msg); return msg.err; } ///////////////////////////////////////////// static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg) { auto *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (*msg->pcb) { tcp_pcb *pcb = *msg->pcb; _reset_tcp_callbacks(pcb); if (tcp_close(pcb) != ERR_OK) { // We do not permit failure here: abandon the pcb anyways. tcp_abort(pcb); } msg->err = ERR_OK; *msg->pcb = nullptr; // PCB is now the property of LwIP } else { // Ensure there is not an error event queued for this client _tcp_clear_events(); msg->err = ERR_OK; // dispose needs to be run } return msg->err; } ///////////////////////////////////////////// static esp_err_t _tcp_close(tcp_pcb **pcb) { if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; msg.pcb = pcb; tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data *)&msg); return msg.err; } ///////////////////////////////////////////// static err_t _tcp_abort_api(struct tcpip_api_call_data *api_call_msg) { auto *msg = (tcp_api_call_t *)api_call_msg; msg->err = ERR_CONN; if (*msg->pcb) { tcp_pcb *pcb = *msg->pcb; _reset_tcp_callbacks(pcb); tcp_abort(pcb); msg->err = ERR_OK; *msg->pcb = nullptr; // PCB is now the property of LwIP } else { // Ensure there is not an error event queued for this client _tcp_clear_events(); } return msg->err; } ///////////////////////////////////////////// static esp_err_t _tcp_abort(tcp_pcb **pcb) { if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; msg.pcb = pcb; tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data *)&msg); return msg.err; } ///////////////////////////////////////////// static err_t _tcp_connect_api(struct tcpip_api_call_data *api_call_msg) { auto *msg = (tcp_api_call_t *)api_call_msg; msg->err = tcp_connect(*msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb); return msg->err; } ///////////////////////////////////////////// static esp_err_t _tcp_connect(tcp_pcb *pcb, ip_addr_t *addr, uint16_t port, tcp_connected_fn cb) { if (!pcb) { return ESP_FAIL; } tcp_api_call_t msg; msg.pcb = &pcb; msg.connect.addr = addr; msg.connect.port = port; msg.connect.cb = cb; tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data *)&msg); return msg.err; } // int tcp_ssl_send(void *ctx, const unsigned char *buf, size_t len) { txtPrintWarn("tcp_ssl_send: ctx: 0x%X, buf: 0x%s, len: %d", (uint32_t)ctx, buf, len); if (ctx == nullptr) { txtPrintError("tcp_ssl_send: no context set"); return -1; } if (buf == nullptr) { txtPrintError("tcp_ssl_send: buf not set"); return -1; } auto *tcp_ssl = (tcp_ssl_t *)ctx; size_t tcp_len = 0; int err = ERR_OK; if (tcp_sndbuf(tcp_ssl->tcp) < len) { tcp_len = tcp_sndbuf(tcp_ssl->tcp); if (tcp_len == 0) { txtPrintError("tcp_ssl_send: tcp_sndbuf is zero: %d", len); return ERR_MEM; } } else { tcp_len = len; } if (tcp_len > 2 * tcp_ssl->tcp->mss) { tcp_len = 2 * tcp_ssl->tcp->mss; } txtPrintWarn("tcp_ssl_send: _tcp_write(%x, %s, %d)", (uint32_t)tcp_ssl->tcp, (char *)buf, tcp_len); err = _tcp_write(&tcp_ssl->tcp, (char *)buf, tcp_len, TCP_WRITE_FLAG_COPY); if (err < ERR_OK) { if (err == ERR_MEM) { txtPrintError("tcp_ssl_send: No memory %d (%d)", tcp_len, len); return err; } txtPrintError("tcp_ssl_send: _tcp_write error: %d", err); return err; } else if (err == ERR_OK) { txtPrintWarn("tcp_ssl_send: tcp_output: %d / %d", tcp_len, len); err = _tcp_output(&tcp_ssl->tcp); if (err != ERR_OK) { txtPrintError("tcp_ssl_send: tcp_output err: %d", err); return err; } } // tcp_ssl->last_wr += tcp_len; return (int)tcp_len; } // int tcp_ssl_recv(void *ctx, unsigned char *buf, size_t len) { auto *tcp_ssl = (tcp_ssl_t *)ctx; if (tcp_ssl->tcp_pbuf == nullptr || tcp_ssl->pbuf_offset >= tcp_ssl->tcp_pbuf->len) { return MBEDTLS_ERR_SSL_WANT_READ; } size_t avail = tcp_ssl->tcp_pbuf->len - tcp_ssl->pbuf_offset; size_t to_copy = (len < avail) ? len : avail; if (to_copy > 0) { memcpy(buf, (const uint8_t *)tcp_ssl->tcp_pbuf->payload + tcp_ssl->pbuf_offset, to_copy); tcp_ssl->pbuf_offset += to_copy; } return (to_copy == 0) ? MBEDTLS_ERR_SSL_WANT_READ : (int)to_copy; } // int tcp_ssl_recv(void *ctx, unsigned char *buf, size_t len) // { // auto *tcp_ssl = (tcp_ssl_t *)ctx; // u16_t recv_len = 0; // if (tcp_ssl->tcp_pbuf == nullptr || tcp_ssl->tcp_pbuf->tot_len == 0) // { // txtPrintError("tcp_ssl_recv: not yet ready to read: tcp_pbuf: 0x%X.", (uint32_t)tcp_ssl->tcp_pbuf); // return MBEDTLS_ERR_SSL_WANT_READ; // } // recv_len = pbuf_copy_partial(tcp_ssl->tcp_pbuf, buf, len, tcp_ssl->pbuf_offset); // tcp_ssl->pbuf_offset += recv_len; // if (recv_len == 0) // { // return MBEDTLS_ERR_SSL_WANT_READ; // } // return recv_len; // } ////////////////////////// /* Async SSL TCP Client */ ////////////////////////// AsyncSSLClient::AsyncSSLClient(tcp_pcb *pcb) : _on_connect_cb(nullptr), _on_connect_cb_arg(nullptr), _on_disconn_cb(nullptr), _on_disconn_cb_arg(nullptr), _on_ask_cb(nullptr), _on_ask_cb_arg(nullptr), _on_error_cb(nullptr), _on_error_cb_arg(nullptr), _on_data_cb(nullptr), _on_data_cb_arg(nullptr), // _on_packet_cb(nullptr), // _on_packet_cb_arg(nullptr), // _on_timeout_cb(nullptr), // _on_timeout_cb_arg(nullptr), _pcb_busy(false), _pcb_sent_at(0), _ack_pcb(true), _rx_ack_len(0), _rx_last_packet(0), _rx_since_timeout(ASYNC_SSL_CLIENT_MAX_RX_TIME), _ack_timeout(ASYNC_SSL_CLIENT_MAX_ACK_TIME), _connect_port(0), // SSL _pcb_secure(false), _handshake_done(true) ////// { _pcb = pcb; if (_pcb) { _rx_last_packet = millis(); tcp_arg(_pcb, this); tcp_recv(_pcb, &AsyncSSLTCP_detail::_tcp_recv); tcp_sent(_pcb, &AsyncSSLTCP_detail::_tcp_sent); tcp_err(_pcb, &AsyncSSLTCP_detail::_tcp_error); tcp_poll(_pcb, &AsyncSSLTCP_detail::_tcp_poll, 1); } } ///////////////////////////////////////////// AsyncSSLClient::~AsyncSSLClient() { if (_pcb) { _close(); } } ////////////////////// /* Callback Setters */ ////////////////////// void AsyncSSLClient::onConnect(AcConnectHandler_ cb, void *arg) { _on_connect_cb = std::move(cb); _on_connect_cb_arg = arg; } ///////////////////////////////////////////// void AsyncSSLClient::onDisconnect(AcConnectHandler_ cb, void *arg) { _on_disconn_cb = std::move(cb); _on_disconn_cb_arg = arg; } ///////////////////////////////////////////// void AsyncSSLClient::onAck(AcAckHandler_ cb, void *arg) { _on_ask_cb = std::move(cb); _on_ask_cb_arg = arg; } ///////////////////////////////////////////// void AsyncSSLClient::onError(AcErrorHandler_ cb, void *arg) { _on_error_cb = std::move(cb); _on_error_cb_arg = arg; } ///////////////////////////////////////////// void AsyncSSLClient::onData(AcDataHandler_ cb, void *arg) { _on_data_cb = std::move(cb); _on_data_cb_arg = arg; } ///////////////////////////////////////////// // void AsyncSSLClient::onPacket(AcPacketHandler_ cb, void *arg) // { // _on_packet_cb = std::move(cb); // _on_packet_cb_arg = arg; // } ///////////////////////////////////////////// // void AsyncSSLClient::onTimeout(AcTimeoutHandler_ cb, void *arg) // { // _on_timeout_cb = std::move(cb); // _on_timeout_cb_arg = arg; // } ///////////////////////////////////////////// void AsyncSSLClient::onPoll(AcConnectHandler_ cb, void *arg) { _on_poll_cb = std::move(cb); _on_poll_cb_arg = arg; } ///////////////////////// /* Main Public Methods */ ///////////////////////// bool AsyncSSLClient::connect(const IPAddress &ip, uint16_t port, bool secure) { if (_pcb) { txtPrintWarn("connect: already connected, state = %s", stateToString()); return false; } if (!_start_async_task()) { txtPrintError("connect: failed to start task"); return false; } ip_addr_t addr; addr.type = IPADDR_TYPE_V4; addr.u_addr.ip4.addr = ip; tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_V4); if (!pcb) { txtPrintError("connect: NULL pcb"); return false; } // SSL _pcb_secure = secure; _handshake_done = !secure; ////// tcp_arg(pcb, this); tcp_err(pcb, &AsyncSSLTCP_detail::_tcp_error); tcp_recv(pcb, &AsyncSSLTCP_detail::_tcp_recv); tcp_sent(pcb, &AsyncSSLTCP_detail::_tcp_sent); tcp_poll(pcb, &AsyncSSLTCP_detail::_tcp_poll, 1); _tcp_connect(pcb, &addr, port, (tcp_connected_fn)&_tcp_connected); return true; } ///////////////////////////////////////////// bool AsyncSSLClient::connect(const char *host, uint16_t port, bool secure) { ip_addr_t addr; if (!_start_async_task()) { txtPrintError("connect: failed to start task"); return false; } err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this); if (err == ERR_OK) { _hostname = host; return connect(IPAddress(addr.u_addr.ip4.addr), port, secure); } else if (err == ERR_INPROGRESS) { _connect_port = port; _hostname = host; _pcb_secure = secure; _handshake_done = !secure; return true; } txtPrintError("connect: error = %d", err); return false; } ///////////////////////////////////////////// void AsyncSSLClient::close(bool now) { if (_pcb && !now) { _tcp_recved(&_pcb, _rx_ack_len); } _close(); } ///////////////////////////////////////////// int8_t AsyncSSLClient::abort() { if (_pcb) { _tcp_abort(&_pcb); _pcb = nullptr; } return ERR_ABRT; } ///////////////////////////////////////////// size_t AsyncSSLClient::space() { if ((_pcb != nullptr) && (_pcb->state == ESTABLISHED)) { return tcp_sndbuf(_pcb); } return 0; } ///////////////////////////////////////////// size_t AsyncSSLClient::add(const char *data, size_t size, uint8_t apiflags) { if (!_pcb || size == 0 || data == nullptr) { return 0; } size_t room = space(); if (!room) { return 0; } if (_pcb_secure) { if (tcp_ssl_client.ssl_ctx->MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) { txtPrintError("tcp_ssl_write: Handshake NOT ready (state: %d)", tcp_ssl_client.ssl_ctx->MBEDTLS_PRIVATE(state)); return 0; } int sent = mbedtls_ssl_write(tcp_ssl_client.ssl_ctx, (uint8_t *)data, size); if (sent == MBEDTLS_ERR_SSL_WANT_WRITE || sent == MBEDTLS_ERR_SSL_WANT_READ) { return 0; } if (sent < 0) { handle_error(sent); if (_on_error_cb) { _on_error_cb(_on_error_cb_arg, this, ERR_WRITE); } _close(); return 0; } if (sent > 0) { txtPrintDebug("add() mbedtls_ssl_write size = %d", sent); return sent; } } size_t will_send = (room < size) ? room : size; int8_t err = ERR_OK; err = (int8_t)_tcp_write(&_pcb, data, will_send, apiflags); if (err != ERR_OK) { return 0; } return will_send; } ///////////////////////////////////////////// bool AsyncSSLClient::send() { // 5 is also OK vTaskDelay(1 / portTICK_PERIOD_MS); int8_t err = ERR_OK; err = (int8_t)_tcp_output(&_pcb); if (err == ERR_OK) { _pcb_busy = true; _pcb_sent_at = millis(); return true; } return false; } ///////////////////////////////////////////// size_t AsyncSSLClient::ack(size_t len) { // 1. Стандартная логика подтверждения приема в lwIP if (len > _rx_ack_len) len = _rx_ack_len; if (len && _pcb) { _tcp_recved(&_pcb, len); } _rx_ack_len -= len; return len; } ///////////////////////////////////////////// // void AsyncSSLClient::ackPacket(struct pbuf *pb) // { // if (!pb) // { // return; // } // _tcp_recved(&_pcb, pb->len); // pbuf_free(pb); // } ////////////////////////// /* Main Private Methods */ ////////////////////////// int8_t AsyncSSLClient::_close() { _rx_ack_len = 0; auto err = (int8_t)_tcp_close(&_pcb); if ((err == ERR_OK) && _on_disconn_cb) { _on_disconn_cb(_on_disconn_cb_arg, this); } // } return err; } /////////////////////// /* Private Callbacks */ /////////////////////// int8_t AsyncSSLClient::_connected(void *pcb, int8_t err) { _pcb = reinterpret_cast<tcp_pcb *>(pcb); if (_pcb) { _rx_last_packet = millis(); _pcb_busy = false; if (_pcb_secure) { int ret = tcp_ssl_new_client(_pcb, _hostname.empty() ? nullptr : _hostname.c_str()); if (ret != ERR_OK) { if (ret == -127) { txtPrintError("_connected: error => closing"); } else { handle_error(ret); } return _close(); } } } // _on_connect_cb happens after SSL handshake if this is a secure connection if (_on_connect_cb && !_pcb_secure) { _on_connect_cb(_on_connect_cb_arg, this); } return ERR_OK; } ///////////////////////////////////////////// void AsyncSSLClient::_error(int8_t err) { if (_on_error_cb) { _on_error_cb(_on_error_cb_arg, this, err); } if (_on_disconn_cb) { _on_disconn_cb(_on_disconn_cb_arg, this); } } ///////////////////////////////////////////// // In LwIP Thread int8_t AsyncSSLClient::_lwip_fin(tcp_pcb *pcb, int8_t err) { if (!_pcb || pcb != _pcb) { txtPrintDebug("_lwip_fin: pcb/_pcb = %p %p", pcb, _pcb); return ERR_OK; } _reset_tcp_callbacks(_pcb); if (tcp_close(_pcb) != ERR_OK) { tcp_abort(_pcb); } _pcb = nullptr; return ERR_OK; } ///////////////////////////////////////////// // In Async Thread int8_t AsyncSSLClient::_fin(tcp_pcb *pcb, int8_t err) { if (_on_disconn_cb) { _on_disconn_cb(_on_disconn_cb_arg, this); } return ERR_OK; } ///////////////////////////////////////////// int8_t AsyncSSLClient::_sent(tcp_pcb *pcb, uint16_t len) { _rx_last_packet = millis(); txtPrintInfo("_sent: len = %u", len); _pcb_busy = false; if (_on_ask_cb) { _on_ask_cb(_on_ask_cb_arg, this, len, (millis() - _pcb_sent_at)); } return ERR_OK; } ///////////////////////////////////////////// int8_t AsyncSSLClient::_recv(tcp_pcb *pcb, pbuf *pb, int8_t err) { while (pb != nullptr) { _rx_last_packet = millis(); pbuf *nxt = pb->next; pb->next = nullptr; txtPrintInfo("_recv: tot_len = %u", pb->tot_len); // we should not ack before we assimilate the data _ack_pcb = true; if (_on_data_cb) { _on_data_cb(_on_data_cb_arg, this, pb->payload, pb->len); } if (!_ack_pcb) { _rx_ack_len += pb->len; } else if (_pcb) { _tcp_recved(&_pcb, pb->len); } pbuf_free(pb); pb = nxt; } return ERR_OK; } static uint8_t _data_buf[16 * 1024]; int8_t AsyncSSLClient::_recv_ssl(tcp_pcb *pcb, pbuf *pb, int8_t err) { while (pb != nullptr) { _rx_last_packet = millis(); pbuf *nxt = pb->next; pb->next = nullptr; txtPrintInfo("_recv_ssl: tot_len = %u", pb->tot_len); // we should not ack before we assimilate the data _ack_pcb = true; tcp_ssl_client.tcp_pbuf = pb; tcp_ssl_client.pbuf_offset = 0; int res = 0; int total = 0; while (tcp_ssl_client.pbuf_offset < pb->tot_len) { if (tcp_ssl_client.ssl_ctx->MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) { res = mbedtls_ssl_handshake(tcp_ssl_client.ssl_ctx); if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) { res = 0; break; } if (res < 0) { txtPrintError("Handshake error"); handle_error(res); res = ERR_HANDSHAKE; break; } if (res == 0) { txtPrintWarn("SSL Handshake DONE! Cipher: %s", mbedtls_ssl_get_ciphersuite(tcp_ssl_client.ssl_ctx)); asyncClient._handshake_done = true; if (asyncClient._on_connect_cb) asyncClient._on_connect_cb(asyncClient._on_connect_cb_arg, &asyncClient); } } else { res = mbedtls_ssl_read(tcp_ssl_client.ssl_ctx, _data_buf + total, sizeof(_data_buf) - total); if (res > 0) { total += res; // Двигаем смещение } else { if (res == MBEDTLS_ERR_SSL_WANT_READ) { res = 0; break; } if (res != MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { txtPrintError("SSL Read error"); handle_error(res); res = ERR_READ; } break; } } } tcp_ssl_client.tcp_pbuf = nullptr; if (total > 0 && _on_data_cb) { _on_data_cb(_on_data_cb_arg, this, _data_buf, total); } if (!_ack_pcb) { _rx_ack_len += pb->len; } else if (pcb) { _tcp_recved(&pcb, pb->len); } pbuf_free(pb); if (res < 0) { if (res != MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { abort(); if (_on_error_cb) { _on_error_cb(_on_error_cb_arg, this, (int8_t)res); } } return ERR_OK; } pb = nxt; } return ERR_OK; } ///////////////////////////////////////////// int8_t AsyncSSLClient::_poll(tcp_pcb *pcb) { if (!_pcb) { txtPrintWarn("_poll: NULL pcb"); return ERR_OK; } if (pcb != _pcb) { txtPrintError("_poll: diff pcb/_pcb = %p %p", pcb, _pcb); return ERR_OK; } uint32_t now = millis(); // ACK Timeout if (_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout) { _pcb_busy = false; txtPrintError("_poll: ack timeout, state = %s", stateToString()); if (_on_error_cb) { _on_error_cb(_on_error_cb_arg, this, ERR_ASK_TIMEOUT); } _close(); return ERR_OK; } // RX Timeout if (_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)) { txtPrintError("_poll: rx timeout, state = %s", stateToString()); if (_on_error_cb) { _on_error_cb(_on_error_cb_arg, this, ERR_RX_TIMEOUT); } _close(); return ERR_OK; } if (_pcb_secure && !_handshake_done && (now - _rx_last_packet) >= ASYNC_SSL_CLIENT_HANDSHAKE_TIMEOUT) { txtPrintError("_poll: ssl handshake timeout, state = %s", stateToString()); if (_on_error_cb) { _on_error_cb(_on_error_cb_arg, this, ERR_HANDSHAKE_TIMEOUT); } _close(); return ERR_OK; } // Everything is fine if (_on_poll_cb) { _on_poll_cb(_on_poll_cb_arg, this); } return ERR_OK; } ///////////////////////////////////////////// void AsyncSSLClient::_dns_found(struct ip_addr *ipaddr) { if (ipaddr && ipaddr->u_addr.ip4.addr) { connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port, _pcb_secure); } else { if (_on_error_cb) { _on_error_cb(_on_error_cb_arg, this, ERR_DNS); } if (_on_disconn_cb) { _on_disconn_cb(_on_disconn_cb_arg, this); } } } /////////////////////////// /* Public Helper Methods */ /////////////////////////// void AsyncSSLClient::stop() { close(false); } ///////////////////////////////////////////// bool AsyncSSLClient::free() { if (!_pcb) { return true; } if ((_pcb->state == CLOSED) || (_pcb->state > ESTABLISHED)) { return true; } return false; } ///////////////////////////////////////////// size_t AsyncSSLClient::write(const char *data) { if (data == nullptr) { return 0; } return write(data, strlen(data)); } ///////////////////////////////////////////// size_t AsyncSSLClient::write(const char *data, size_t size, uint8_t apiflags) { size_t will_send = add(data, size, apiflags); if (!will_send || !send()) { return 0; } return will_send; } ///////////////////////////////////////////// void AsyncSSLClient::setRxTimeout(uint32_t timeout) { _rx_since_timeout = timeout; } ///////////////////////////////////////////// uint32_t AsyncSSLClient::getRxTimeout() { return _rx_since_timeout; } ///////////////////////////////////////////// uint32_t AsyncSSLClient::getAckTimeout() { return _ack_timeout; } ///////////////////////////////////////////// void AsyncSSLClient::setAckTimeout(uint32_t timeout) { _ack_timeout = timeout; } ///////////////////////////////////////////// void AsyncSSLClient::setNoDelay(bool nodelay) { if (!_pcb) { return; } if (nodelay) { tcp_nagle_disable(_pcb); } else { tcp_nagle_enable(_pcb); } } ///////////////////////////////////////////// bool AsyncSSLClient::getNoDelay() { if (!_pcb) { return false; } return tcp_nagle_disabled(_pcb); } ///////////////////////////////////////////// uint16_t AsyncSSLClient::getMss() { if (!_pcb) { return 0; } return tcp_mss(_pcb); } ///////////////////////////////////////////// uint32_t AsyncSSLClient::getRemoteAddress() { if (!_pcb) { return 0; } return _pcb->remote_ip.u_addr.ip4.addr; } ///////////////////////////////////////////// uint16_t AsyncSSLClient::getRemotePort() { if (!_pcb) { return 0; } return _pcb->remote_port; } ///////////////////////////////////////////// uint32_t AsyncSSLClient::getLocalAddress() { if (!_pcb) { return 0; } return _pcb->local_ip.u_addr.ip4.addr; } ///////////////////////////////////////////// uint16_t AsyncSSLClient::getLocalPort() { if (!_pcb) { return 0; } return _pcb->local_port; } ///////////////////////////////////////////// IPAddress AsyncSSLClient::remoteIP() { return {getRemoteAddress()}; } ///////////////////////////////////////////// uint16_t AsyncSSLClient::remotePort() { return getRemotePort(); } ///////////////////////////////////////////// IPAddress AsyncSSLClient::localIP() { return {getLocalAddress()}; } ///////////////////////////////////////////// uint16_t AsyncSSLClient::localPort() { return getLocalPort(); } ///////////////////////////////////////////// uint8_t AsyncSSLClient::state() { if (!_pcb) { return 0; } return (_pcb->state); } ///////////////////////////////////////////// bool AsyncSSLClient::connected() { if (!_pcb) { return false; } if (_pcb_secure) { return _pcb->state == ESTABLISHED && _handshake_done; } return (_pcb->state == ESTABLISHED); } ///////////////////////////////////////////// bool AsyncSSLClient::connecting() { if (!_pcb) { return false; } return (_pcb->state > CLOSED && _pcb->state < ESTABLISHED); } ///////////////////////////////////////////// bool AsyncSSLClient::disconnecting() { if (!_pcb) { return false; } if (_pcb_secure) { return !_handshake_done; } return (_pcb->state > ESTABLISHED && _pcb->state < TIME_WAIT); } ///////////////////////////////////////////// bool AsyncSSLClient::disconnected() { if (!_pcb) { return true; } if (_pcb_secure) { return !_handshake_done; } return (_pcb->state == CLOSED || _pcb->state == TIME_WAIT); } ///////////////////////////////////////////// // bool AsyncSSLClient::freeable() // { // if (!_pcb) // { // return true; // } // return (_pcb->state == CLOSED || _pcb->state > ESTABLISHED); // } ///////////////////////////////////////////// bool AsyncSSLClient::canSend() { return space() > 0; } ///////////////////////////////////////////// const char *AsyncSSLClient::errorToString(int8_t error) { switch (error) { case ERR_OK: return "OK"; case ERR_MEM: return "Out of memory error"; case ERR_BUF: return "Buffer error"; case ERR_TIMEOUT: return "Timeout"; case ERR_RTE: return "Routing problem"; case ERR_INPROGRESS: return "Operation in progress"; case ERR_VAL: return "Illegal value"; case ERR_WOULDBLOCK: return "Operation would block"; case ERR_USE: return "Address in use"; case ERR_ALREADY: return "Already connected"; case ERR_ISCONN: case ERR_CONN: return "Not connected"; case ERR_IF: return "Low-level netif error"; case ERR_ABRT: return "Connection aborted"; case ERR_RST: return "Connection reset"; case ERR_CLSD: return "Connection closed"; case ERR_ARG: return "Illegal argument"; case ERR_WRITE: return "SSL Write error"; case ERR_READ: return "SSL Read error"; case ERR_HANDSHAKE: return "SSL Handshake error"; case ERR_DNS: return "DNS failed"; case ERR_ASK_TIMEOUT: return "ASK timeout"; case ERR_RX_TIMEOUT: return "RX timeout"; case ERR_HANDSHAKE_TIMEOUT: return "SSL Handshake timeout"; default: return "UNKNOWN"; } } ///////////////////////////////////////////// const char *AsyncSSLClient::stateToString() { switch (state()) { case CLOSED: return "Closed"; case LISTEN: return "Listen"; case SYN_SENT: return "SYN Sent"; case SYN_RCVD: return "SYN Received"; case ESTABLISHED: return "Established"; case FIN_WAIT_1: return "FIN Wait 1"; case FIN_WAIT_2: return "FIN Wait 2"; case CLOSE_WAIT: return "Close Wait"; case CLOSING: return "Closing"; case LAST_ACK: return "Last ACK"; case TIME_WAIT: return "Time Wait"; default: return "UNKNOWN"; } } /////////////////////////////////////////////