diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cec4d51..f87e9a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,8 +14,8 @@ If you don't find anything, please [open a new issue](https://github.com/khoih-p Please ensure to specify the following: -* Arduino IDE version (e.g. 1.8.16) or Platform.io version -* `ESP32` Core Version (e.g. ESP32 core v2.0.0) +* Arduino IDE version (e.g. 1.8.19) or Platform.io version +* `ESP32` Core Version (e.g. ESP32 core v2.0.2) * `ESP32` Board type (e.g. ESP32_DEV, ESP32S2_DEV Module, ESP32_S2_Saola, etc.) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce @@ -27,11 +27,11 @@ Please ensure to specify the following: ### Example ``` -Arduino IDE version: 1.8.16 -ESP32 core v2.0.0 +Arduino IDE version: 1.8.19 +ESP32 core v2.0.2 ESP32_DEV Module OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.4.0-86-generic #97-Ubuntu SMP Fri Sep 17 19:19:40 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.4.0-96-generic #109-Ubuntu SMP Wed Jan 12 16:49:16 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered a crash while using TimerInterrupt. diff --git a/README.md b/README.md index 4df589f..8be63d4 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ## Table of contents -* [Table of contents](#table-of-contents) +* [Important Change from v1.2.0](#Important-Change-from-v120) * [Why do we need this AsyncTCP_SSL library](#why-do-we-need-this-AsyncTCP_SSL-library) * [Features](#features) * [Why Async is better](#why-async-is-better) @@ -31,6 +31,8 @@ * [3. ESP32 WiFi uses ADC2 for WiFi functions](#3-esp32-wifi-uses-adc2-for-wifi-functions) * [Orignal documentation](#Orignal-documentation) * [AsyncSSLClient](#AsyncSSLClient) +* [Examples](#examples) + * [1. multiFileProject](examples/multiFileProject) * [Debug Terminal Output Samples](#debug-terminal-output-samples) * [1. AsyncHTTPSRequest_ESP on ESP32_DEV](#1-AsyncHTTPSRequest_ESP-on-ESP32_DEV) * [2. AsyncHTTPSRequest_ESP on ESP32S2_DEV](#2-AsyncHTTPSRequest_ESP-on-ESP32S2_DEV) @@ -49,6 +51,10 @@ --- --- +### Important Change from v1.2.0 + +Please have a look at [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) + ### Why do we need this [AsyncTCP_SSL library](https://github.com/khoih-prog/AsyncTCP_SSL) #### Features @@ -90,8 +96,8 @@ to apply the better and faster **asynchronous** feature of the **powerful** [Asy ## Prerequisites - 1. [`Arduino IDE 1.8.16+` for Arduino](https://www.arduino.cc/en/Main/Software) - 2. [`ESP32 Core 2.0.0+`](https://github.com/espressif/arduino-esp32) for ESP32-based boards. [![Latest release](https://img.shields.io/github/release/espressif/arduino-esp32.svg)](https://github.com/espressif/arduino-esp32/releases/latest/) + 1. [`Arduino IDE 1.8.19+` for Arduino](https://github.com/arduino/Arduino). [![GitHub release](https://img.shields.io/github/release/arduino/Arduino.svg)](https://github.com/arduino/Arduino/releases/latest) + 2. [`ESP32 Core 2.0.2+`](https://github.com/espressif/arduino-esp32) for ESP32-based boards. [![Latest release](https://img.shields.io/github/release/espressif/arduino-esp32.svg)](https://github.com/espressif/arduino-esp32/releases/latest/) --- --- @@ -148,13 +154,23 @@ Thanks to [Roshan](https://github.com/solroshan) to report the issue in [Error e ### HOWTO Fix `Multiple Definitions` Linker Error -The current library implementation, using xyz-Impl.h instead of standard xyz.cpp, possibly creates certain `Multiple Definitions` Linker error in certain use cases. Although it's simple to just modify several lines of code, either in the library or in the application, the library is adding a separate source directory, named src_cpp, besides the standard src directory. +The current library implementation, using `xyz-Impl.h` instead of standard `xyz.cpp`, possibly creates certain `Multiple Definitions` Linker error in certain use cases. + +You can include this `.hpp` file + +``` +// Can be included as many times as necessary, without `Multiple Definitions` Linker Error +#include "AsyncTCP_SSL.hpp" //https://github.com/khoih-prog/AsyncTCP_SSL +``` -To use the old standard cpp way, just +in many files. But be sure to use the following `.h` file **in just 1 `.h`, `.cpp` or `.ino` file**, which must **not be included in any other file**, to avoid `Multiple Definitions` Linker Error + +``` +// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error +#include "AsyncTCP_SSL.h" //https://github.com/khoih-prog/AsyncTCP_SSL +``` -1. **Rename the h-only src directory into src_h.** -2. **Then rename the cpp src_cpp directory into src.** -3. Close then reopen the application code in Arduino IDE, etc. to recompile from scratch. +Check the new [**multiFileProject** example](examples/multiFileProject) for a `HOWTO` demo. --- --- @@ -232,6 +248,12 @@ This is a fully asynchronous SSL TCP library, aimed at enabling trouble-free, mu The base classes on which everything else is built. They expose all possible scenarios, but are really raw and require more skills to use. +--- +--- + +### Examples + + 1. [multiFileProject](examples/multiFileProject). **New** --- --- @@ -244,47 +266,46 @@ Following is the debug terminal when running example [AsyncHTTPSRequest_ESP](htt ``` Starting AsyncHTTPSRequest_ESP using ESP32_DEV -AsyncTCP_SSL v1.1.0 -AsyncHTTPSRequest_Generic v1.0.0 +AsyncTCP_SSL v1.2.0 +AsyncHTTPSRequest_Generic v1.3.0 Connecting to WiFi SSID: HueNet1 ....... -AsyncHTTPSRequest @ IP : 192.168.2.78 +AsyncHTTPSRequest @ IP : 192.168.2.133 ************************************** -abbreviation: EDT +abbreviation: EST client_ip: aaa.bbb.ccc.ddd -datetime: 2021-10-21T16:05:03.170256-04:00 -day_of_week: 4 -day_of_year: 294 -dst: true -dst_from: 2021-03-14T07:00:00+00:00 -dst_offset: 3600 -dst_until: 2021-11-07T06:00:00+00:00 +datetime: 2022-01-23T21:17:05.405121-05:00 +day_of_week: 0 +day_of_year: 23 +dst: false +dst_from: +dst_offset: 0 +dst_until: raw_offset: -18000 timezone: America/Toronto -unixtime: 1634846703 -utc_datetime: 2021-10-21T20:05:03.170256+00:00 -utc_offset: -04:00 -week_number: 42 +unixtime: 1642990625 +utc_datetime: 2022-01-24T02:17:05.405121+00:00 +utc_offset: -05:00 +week_number: 3 ************************************** HHHHHH ************************************** -abbreviation: EDT +abbreviation: EST client_ip: aaa.bbb.ccc.ddd -datetime: 2021-10-21T16:06:00.828056-04:00 -day_of_week: 4 -day_of_year: 294 -dst: true -dst_from: 2021-03-14T07:00:00+00:00 -dst_offset: 3600 -dst_until: 2021-11-07T06:00:00+00:00 +datetime: 2022-01-23T21:18:03.759271-05:00 +day_of_week: 0 +day_of_year: 23 +dst: false +dst_from: +dst_offset: 0 +dst_until: raw_offset: -18000 timezone: America/Toronto -unixtime: 1634846760 -utc_datetime: 2021-10-21T20:06:00.828056+00:00 -utc_offset: -04:00 -week_number: 42 -************************************** +unixtime: 1642990683 +utc_datetime: 2022-01-24T02:18:03.759271+00:00 +utc_offset: -05:00 +week_number: 3 ``` --- @@ -294,8 +315,8 @@ Following is the debug terminal when running example [AsyncHTTPSRequest_ESP](htt ``` Starting AsyncHTTPSRequest_ESP using ESP32S2_DEV -AsyncTCP_SSL v1.1.0 -AsyncHTTPSRequest_Generic v1.0.0 +AsyncTCP_SSL v1.2.0 +AsyncHTTPSRequest_Generic v1.3.0 Connecting to WiFi SSID: HueNet1 ....... AsyncHTTPSRequest @ IP : 192.168.2.79 @@ -323,23 +344,22 @@ AsyncHTTPSRequest @ IP : 192.168.2.79 [ATCP] _sent: len = 106 [ATCP] _handle_async_event: LWIP_TCP_RECV = 0x3FFE5024 [ATCP] _recv: tot_len = 1016 - ************************************** -abbreviation: EDT +abbreviation: EST client_ip: aaa.bbb.ccc.ddd -datetime: 2021-10-21T23:19:43.835205-04:00 -day_of_week: 4 -day_of_year: 294 -dst: true -dst_from: 2021-03-14T07:00:00+00:00 -dst_offset: 3600 -dst_until: 2021-11-07T06:00:00+00:00 +datetime: 2022-01-23T21:21:03.766116-05:00 +day_of_week: 0 +day_of_year: 23 +dst: false +dst_from: +dst_offset: 0 +dst_until: raw_offset: -18000 timezone: America/Toronto -unixtime: 1634872783 -utc_datetime: 2021-10-22T03:19:43.835205+00:00 -utc_offset: -04:00 -week_number: 42 +unixtime: 1642990863 +utc_datetime: 2022-01-24T02:21:03.766116+00:00 +utc_offset: -05:00 +week_number: 3 ************************************** ``` @@ -351,28 +371,27 @@ Following is the debug terminal when running example [AsyncHTTPSRequest_ESP](htt ``` Starting AsyncHTTPSRequest_ESP using ESP32C3_DEV -AsyncTCP_SSL v1.1.0 -AsyncHTTPSRequest_Generic v1.0.0 +AsyncTCP_SSL v1.2.0 +AsyncHTTPSRequest_Generic v1.3.0 Connecting to WiFi SSID: HueNet1 ......... AsyncHTTPSRequest @ IP : 192.168.2.80 - ************************************** -abbreviation: EDT +abbreviation: EST client_ip: aaa.bbb.ccc.ddd -datetime: 2021-10-22T02:00:44.009661-04:00 -day_of_week: 5 -day_of_year: 295 -dst: true -dst_from: 2021-03-14T07:00:00+00:00 -dst_offset: 3600 -dst_until: 2021-11-07T06:00:00+00:00 +datetime: 2022-01-23T21:24:07.839337-05:00 +day_of_week: 0 +day_of_year: 23 +dst: false +dst_from: +dst_offset: 0 +dst_until: raw_offset: -18000 timezone: America/Toronto -unixtime: 1634882444 -utc_datetime: 2021-10-22T06:00:44.009661+00:00 -utc_offset: -04:00 -week_number: 42 +unixtime: 1642991047 +utc_datetime: 2022-01-24T02:24:07.839337+00:00 +utc_offset: -05:00 +week_number: 3 ************************************** ``` @@ -384,30 +403,30 @@ Following is the debug terminal when running example [AsyncHTTPSRequest_ESP_WiFi ``` Starting AsyncHTTPSRequest_ESP_WiFiManager using LittleFS on ESP32_DEV -ESPAsync_WiFiManager v1.9.4 -AsyncTCP_SSL v1.1.0 -AsyncHTTPSRequest_Generic v1.0.0 +ESPAsync_WiFiManager v1.11.0 +AsyncTCP_SSL v1.2.0 +AsyncHTTPSRequest_Generic v1.3.0 Stored: SSID = HueNet1, Pass = 12345678 Got stored Credentials. Timeout 120s ConnectMultiWiFi in setup After waiting 11.38 secs more in setup(), connection result is connected. Local IP: 192.168.2.232 H ************************************** -abbreviation: EDT +abbreviation: EST client_ip: aaa.bbb.ccc.ddd -datetime: 2021-10-22T02:38:12.722777-04:00 -day_of_week: 5 -day_of_year: 295 -dst: true -dst_from: 2021-03-14T07:00:00+00:00 -dst_offset: 3600 -dst_until: 2021-11-07T06:00:00+00:00 +datetime: 2022-01-23T21:18:03.759271-05:00 +day_of_week: 0 +day_of_year: 23 +dst: false +dst_from: +dst_offset: 0 +dst_until: raw_offset: -18000 timezone: America/Toronto -unixtime: 1634884692 -utc_datetime: 2021-10-22T06:38:12.722777+00:00 -utc_offset: -04:00 -week_number: 42 +unixtime: 1642990683 +utc_datetime: 2022-01-24T02:18:03.759271+00:00 +utc_offset: -05:00 +week_number: 3 ************************************** H ``` @@ -447,6 +466,7 @@ Submit issues to: [AsyncTCP_SSL issues](https://github.com/khoih-prog/AsyncTCP_S 1. Search for bug and improvement. 2. Similar Async SSL libraries for ESP8266, STM32, Portenta_H7 and many other boards +3. Permit both HTTP and HTTPS --- @@ -455,6 +475,8 @@ Submit issues to: [AsyncTCP_SSL issues](https://github.com/khoih-prog/AsyncTCP_S 1. Add support to ESP32 using SSL 2. Add Table of Contents 3. Add debug feature +4. Fix `multiple-definitions` linker error +5. Add example --- --- diff --git a/changelog.md b/changelog.md index c34a610..607a9df 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ ## Table of Contents * [Changelog](#changelog) + * [Releases v1.2.0](#Releases-v120) * [Releases v1.1.0](#Releases-v110) * [Initial Releases v1.0.0](#Initial-Releases-v100) @@ -19,6 +20,12 @@ ## Changelog +### Releases v1.2.0 + +1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories +2. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project. +3. Add `platformio.ini` + ### Releases v1.1.0 1. Fix duplication bug when using `src_h` diff --git a/examples/multiFileProject/multiFileProject.cpp b/examples/multiFileProject/multiFileProject.cpp new file mode 100644 index 0000000..f9d4c3d --- /dev/null +++ b/examples/multiFileProject/multiFileProject.cpp @@ -0,0 +1,13 @@ +/**************************************************************************************************************************** + multiFileProject.cpp + For ESP32 + + AsyncTCP_SSL is a library for the ESP32 + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncTCP_SSL + Licensed under MIT license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#include "multiFileProject.h" diff --git a/examples/multiFileProject/multiFileProject.h b/examples/multiFileProject/multiFileProject.h new file mode 100644 index 0000000..b33cbe5 --- /dev/null +++ b/examples/multiFileProject/multiFileProject.h @@ -0,0 +1,16 @@ +/**************************************************************************************************************************** + multiFileProject.h + For ESP32 + + AsyncTCP_SSL is a library for the ESP32 + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncTCP_SSL + Licensed under MIT license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#pragma once + +// Can be included as many times as necessary, without `Multiple Definitions` Linker Error +#include "AsyncTCP_SSL.hpp" diff --git a/examples/multiFileProject/multiFileProject.ino b/examples/multiFileProject/multiFileProject.ino new file mode 100644 index 0000000..90b9814 --- /dev/null +++ b/examples/multiFileProject/multiFileProject.ino @@ -0,0 +1,51 @@ +/**************************************************************************************************************************** + multiFileProject.ino + For ESP32 + + AsyncTCP_SSL is a library for the ESP32 + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncTCP_SSL + Licensed under MIT license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#if !( defined(ESP32) ) + #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. +#endif + +#define ASYNC_TCP_SSL_VERSION_MIN_TARGET "AsyncTCP_SSL v1.2.0" +#define ASYNC_TCP_SSL_VERSION_MIN 1002000 + +#include "multiFileProject.h" + +#include + +// Can be included as many times as necessary, without `Multiple Definitions` Linker Error +#include "AsyncTCP_SSL.h" + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(500); + + Serial.println("\nStart multiFileProject"); + Serial.println(ASYNC_TCP_SSL_VERSION); + +#if defined(ASYNC_TCP_SSL_VERSION_MIN) + if (ASYNC_TCP_SSL_VERSION_INT < ASYNC_TCP_SSL_VERSION_MIN) + { + Serial.print("Warning. Must use this example on Version equal or later than : "); + Serial.println(ASYNC_TCP_SSL_VERSION_MIN_TARGET); + } +#endif + + Serial.print("You're OK now"); +} + +void loop() +{ + // put your main code here, to run repeatedly: +} diff --git a/library.json b/library.json index a2fd58f..16298f0 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name":"AsyncTCP_SSL", - "version": "1.1.0", + "version": "1.2.0", "keywords":"communication, async, tcp, ssl, tls, mbed, free-rtos", "description":"Asynchronous SSL TCP Library for ESP32. This library is the base for future and more advanced Async SSL libraries, such as AsyncSSLWebServer, AsyncHTTPSRequest", "authors": @@ -37,8 +37,9 @@ "tests" ] }, + "license": "LGPL-3.0", "frameworks": "*", "platforms": "espressif32", "examples": "examples/*/*/*.ino", - "license": "LGPL-3.0" + "headers": ["AsyncTCP_SSL.h", "AsyncTCP_SSL.hpp"] } diff --git a/library.properties b/library.properties index 5d5ab9c..57d0aec 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AsyncTCP_SSL -version=1.1.0 +version=1.2.0 author=Hristo Gochkov, Maarten Fremouw, Thorsten von Eicken, Khoi Hoang maintainer=Khoi Hoang sentence=Asynchronous SSL TCP Library for ESP32. @@ -9,4 +9,4 @@ url=https://github.com/khoih-prog/AsyncTCP_SSL architectures=esp32 repository=https://github.com/khoih-prog/AsyncTCP_SSL license=LGPLv3 -includes=AsyncTCP_SSL.h +includes=AsyncTCP_SSL.h,AsyncTCP_SSL.hpp diff --git a/platformio/platformio.ini b/platformio/platformio.ini new file mode 100644 index 0000000..df82b4a --- /dev/null +++ b/platformio/platformio.ini @@ -0,0 +1,337 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +; ============================================================ +; chose environment: +; ESP8266 +; ESP32 +; SAMD +; NRF52 +; STM32 +; ============================================================ +;default_envs = ESP8266 +default_envs = ESP32 +;default_envs = SAMD +;default_envs = NRF52 +;default_envs = STM32 + +[env] +; ============================================================ +; Serial configuration +; choose upload speed, serial-monitor speed +; ============================================================ +upload_speed = 921600 +;upload_port = COM11 +;monitor_speed = 9600 +;monitor_port = COM11 + +; Checks for the compatibility with frameworks and dev/platforms +lib_compat_mode = strict + +lib_deps = + +build_flags = +; set your debug output (default=Serial) +; -D DEBUG_ESP_PORT=Serial +; comment the folowing line to enable WiFi debugging +; -D NDEBUG + +[env:ESP8266] +platform = espressif8266 +framework = arduino +; ============================================================ +; Board configuration +; choose your board by uncommenting one of the following lines +; ============================================================ +;board = gen4iod +;board = huzzah +;board = oak +;board = esp_wroom_02 +;board = espduino +;board = espectro +;board = espino +;board = espresso_lite_v1 +;board = espresso_lite_v2 +;board = esp12e +;board = esp01_1m +;board = esp01 +;board = esp07 +;board = esp8285 +;board = heltec_wifi_kit_8 +;board = inventone +;board = nodemcu +board = nodemcuv2 +;board = modwifi +;board = phoenix_v1 +;board = phoenix_v2 +;board = sparkfunBlynk +;board = thing +;board = thingdev +;board = esp210 +;board = espinotee +;board = d1 +;board = d1_mini +;board = d1_mini_lite +;board = d1_mini_pro +;board = wifi_slot +;board = wifiduino +;board = wifinfo +;board = wio_link +;board = wio_node +;board = xinabox_cw01 +;board = esp32doit-devkit-v1 + +[env:ESP32] +platform = espressif32 +framework = arduino +; ============================================================ +; Board configuration +; choose your board by uncommenting one of the following lines +; ============================================================ +;board = esp32cam +;board = alksesp32 +;board = featheresp32 +;board = espea32 +;board = bpi-bit +;board = d-duino-32 +board = esp32doit-devkit-v1 +;board = pocket_32 +;board = fm-devkit +;board = pico32 +;board = esp32-evb +;board = esp32-gateway +;board = esp32-pro +;board = esp32-poe +;board = oroca_edubot +;board = onehorse32dev +;board = lopy +;board = lopy4 +;board = wesp32 +;board = esp32thing +;board = sparkfun_lora_gateway_1-channel +;board = ttgo-lora32-v1 +;board = ttgo-t-beam +;board = turta_iot_node +;board = lolin_d32 +;board = lolin_d32_pro +;board = lolin32 +;board = wemosbat +;board = widora-air +;board = xinabox_cw02 +;board = iotbusio +;board = iotbusproteus +;board = nina_w10 + +[env:SAMD] +platform = atmelsam +framework = arduino +; ============================================================ +; Choose your board by uncommenting one of the following lines +; ============================================================ +; ============================================================ +; Board configuration Adafruit SAMD +; ============================================================ + +;board = adafruit_feather_m0 +;board = adafruit_feather_m0_express +;board = adafruit_metro_m0 +;board = adafruit_circuitplayground_m0 +;board = adafruit_gemma_m0 +;board = adafruit_trinket_m0 +;board = adafruit_itsybitsy_m0 +;board = adafruit_pirkey +;board = adafruit_hallowing +;board = adafruit_crickit_m0 +;board = adafruit_metro_m4 +;board = adafruit_grandcentral_m4 +board = adafruit_itsybitsy_m4 +;board = adafruit_feather_m4 +;board = adafruit_trellis_m4 +;board = adafruit_pyportal_m4 +;board = adafruit_pyportal_m4_titano +;board = adafruit_pybadge_m4 +;board = adafruit_metro_m4_airliftlite +;board = adafruit_pygamer_m4 +;board = adafruit_pygamer_advance_m4 +;board = adafruit_pybadge_airlift_m4 +;board = adafruit_monster_m4sk +;board = adafruit_hallowing_m4 + +; ============================================================ +; Board configuration Arduino SAMD and SAM +; ============================================================ + +;board = arduino_zero_edbg +;board = arduino_zero_native +;board = mkr1000 +;board = mkrzero +;board = mkrwifi1010 +;board = nano_33_iot +;board = mkrfox1200 +;board = mkrwan1300 +;board = mkrwan1310 +;board = mkrgsm1400 +;board = mkrnb1500 +;board = mkrvidor4000 +;board = adafruit_circuitplayground_m0 +;board = mzero_pro_bl_dbg +;board = mzero_pro_bl +;board = mzero_bl +;board = tian +;board = tian_cons +;board = arduino_due_x_dbg +;board = arduino_due_x + +; ============================================================ +; Board configuration Seeeduino SAMD +; ============================================================ + +;board = seeed_wio_terminal +;board = Seeed_femto_m0 +;board = seeed_XIAO_m0 +;board = Wio_Lite_MG126 +;board = WioGPS +;board = zero +;board = rolawan +;board = seeed_grove_ui_wireless + + +[env:NRF52] +platform = nordicnrf52 +framework = arduino +; ============================================================ +; Board configuration Adafruit nRF52 +; choose your board by uncommenting one of the following lines +; ============================================================ +;board = feather52832 +board = feather52840 +;board = feather52840sense +;board = itsybitsy52840 +;board = cplaynrf52840 +;board = cluenrf52840 +;board = metro52840 +;board = pca10056 +;board = particle_xenon +;board = mdbt50qrx +;board = ninab302 +;board = ninab112 + +[env:STM32] +platform = ststm32 +framework = arduino + +; ============================================================ +; Choose your board by uncommenting one of the following lines +; ============================================================ + +; ============================================================ +; Board configuration Nucleo-144 +; ============================================================ + +;board = nucleo_f207zg +;board = nucleo_f429zi +;board = nucleo_f746zg +;board = nucleo_f756zg +;board = nucleo_f767zi +;board = nucleo_h743zi +;board = nucleo_l496zg +;board = nucleo_l496zg-p +;board = nucleo_l4r5zi +;board = nucleo_l4r5zi-p + +; ============================================================ +; Board configuration Nucleo-64 +; ============================================================ + +;board = nucleo_f030r8 +;board = nucleo_f072rb + +;board = nucleo_f091rc +;board = nucleo_f103rb +;board = nucleo_f302r8 +;board = nucleo_f303re +;board = nucleo_f401re +;board = nucleo_f411re +;board = nucleo_f446re +;board = nucleo_g071rb +;board = nucleo_g431rb +;board = nucleo_g474re +;board = nucleo_l053r8 +;board = nucleo_l073rz +;board = nucleo_l152re +;board = nucleo_l433rc_p +;board = nucleo_l452re +;board = nucleo_l452re-p +;board = nucleo_l476rg +;board = pnucleo_wb55rg + +; ============================================================ +; Board configuration Nucleo-32 +; ============================================================ + +;board = nucleo_f031k6 +;board = nucleo_l031k6 +;board = nucleo_l412kb +;board = nucleo_l432lc +;board = nucleo_f303k8 +;board = nucleo_g431kb + +; ============================================================ +; Board configuration Discovery Boards +; ============================================================ + +;board = disco_f030r8 +;board = disco_f072rb +;board = disco_f030r8 +;board = disco_f100rb +;board = disco_f407vg +;board = disco_f413zh +;board = disco_f746ng +;board = disco_g0316 +;board = disco_l475vg_iot +;board = disco_f072cz-lrwan1 + +; ============================================================ +; Board configuration STM32MP1 Boards +; ============================================================ + +;board = stm32mp157a-dk1 +;board = stm32mp157c-dk2 + +; ============================================================ +; Board configuration Generic Boards +; ============================================================ + +;board = bluepill_f103c6 +;board = bluepill_f103c8 +;board = blackpill_f103c8 +;board = stm32f103cx +;board = stm32f103rx +;board = stm32f103tx +;board = stm32f103vx +;board = stm32f103zx +;board = stm32f103zet6 +;board = maplemini_f103cb +;board = blackpill_f303cc +;board = black_f407ve +;board = black_f407vg +;board = black_f407ze +;board = black_f407zg +;board = blue_f407ve_mini +;board = blackpill_f401cc +;board = blackpill_f411ce +;board = coreboard_f401rc +;board = feather_f405 + +; ============================================================ +; Board configuration Many more Boards to be filled +; ============================================================ + diff --git a/src/AsyncTCP_SSL.h b/src/AsyncTCP_SSL.h index 85e82ac..8bdf80f 100644 --- a/src/AsyncTCP_SSL.h +++ b/src/AsyncTCP_SSL.h @@ -15,12 +15,13 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - Version: 1.1.0 + Version: 1.2.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 21/10/2021 Initial coding to support only ESP32 1.1.0 K Hoang 22/10/2021 Fix bug. Enable coexistence with AsyncTCP + 1.2.0 K Hoang 23/01/2022 Fix `multiple-definitions` linker error *****************************************************************************************************************************/ /* @@ -47,333 +48,7 @@ #ifndef ASYNCTCP_SSL_H_ #define ASYNCTCP_SSL_H_ -#if !(ESP32) - #error This AsyncTCP_SSL library is supporting only ESP32 -#endif - -#define ASYNC_TCP_SSL_VERSION "AsyncTCP_SSL v1.1.0" - -#define ASYNC_TCP_SSL_ENABLED true - -#include "IPAddress.h" -#include "sdkconfig.h" -#include -#include - -// in ./libraries/WiFiClientSecure/src/ssl_client.h for ESP32 -#include - -#include "tcp_mbedtls.h" - -extern "C" -{ - #include "freertos/semphr.h" - #include "lwip/pbuf.h" -} - -#include "AsyncTCP_SSL_Debug.h" - -//If core is not defined, then we are running in Arduino or PIO -#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE - #define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core - #define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event -#endif - -class AsyncSSLClient; - -#define ASYNC_MAX_ACK_TIME 5000 -#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given) -#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react. -#define SSL_HANDSHAKE_TIMEOUT 5000 // timeout to complete SSL handshake - -#if ASYNC_TCP_SSL_ENABLED -typedef std::function AcConnectHandlerSSL; -typedef std::function AcAckHandlerSSL; -typedef std::function AcErrorHandlerSSL; -typedef std::function AcDataHandlerSSL; -typedef std::function AcPacketHandlerSSL; -typedef std::function AcTimeoutHandlerSSL; -#else -typedef std::function AcConnectHandler; -typedef std::function AcAckHandler; -typedef std::function AcErrorHandler; -typedef std::function AcDataHandler; -typedef std::function AcPacketHandler; -typedef std::function AcTimeoutHandler; -#endif - -struct tcp_pcb; -struct ip_addr; - -class AsyncSSLClient -{ - public: - AsyncSSLClient(tcp_pcb* pcb = 0); - ~AsyncSSLClient(); - - AsyncSSLClient & operator=(const AsyncSSLClient &other); - AsyncSSLClient & operator+=(const AsyncSSLClient &other); - - bool operator==(const AsyncSSLClient &other); - - bool operator!=(const AsyncSSLClient &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 setClientCert(const char* cli_cert, const size_t len); - void setClientKey(const char* cli_key, 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(); - bool free(); - - bool canSend();//ack is not pending - size_t space();//space available in the TCP window - size_t add(const char* data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); //add for sending - bool send();//send all data added with the method above - - //write equals add()+send() - size_t write(const char* data); - size_t write(const char* data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); //only when canSend() == true - - uint8_t state(); - bool connecting(); - bool connected(); - bool disconnecting(); - bool disconnected(); - bool freeable();//disconnected or disconnecting - - uint16_t getMss(); - - uint32_t getRxTimeout(); - void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds - - uint32_t getAckTimeout(); - void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds - - void setNoDelay(bool nodelay); - bool getNoDelay(); - - uint32_t getRemoteAddress(); - uint16_t getRemotePort(); - uint32_t getLocalAddress(); - uint16_t getLocalPort(); - - //compatibility - IPAddress remoteIP(); - uint16_t remotePort(); - IPAddress localIP(); - uint16_t localPort(); - -#if ASYNC_TCP_SSL_ENABLED - void onConnect(AcConnectHandlerSSL cb, void* arg = 0); //on successful connect - void onDisconnect(AcConnectHandlerSSL cb, void* arg = 0); //disconnected - void onAck(AcAckHandlerSSL cb, void* arg = 0); //ack received - void onError(AcErrorHandlerSSL cb, void* arg = 0); //unsuccessful connect or error - void onData(AcDataHandlerSSL cb, void* arg = 0); //data received (called if onPacket is not used) - void onPacket(AcPacketHandlerSSL cb, void* arg = 0); //data received - void onTimeout(AcTimeoutHandlerSSL cb, void* arg = 0); //ack timeout - void onPoll(AcConnectHandlerSSL cb, void* arg = 0); //every 125ms when connected -#else - void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect - void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected - void onAck(AcAckHandler cb, void* arg = 0); //ack received - void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error - void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used) - void onPacket(AcPacketHandler cb, void* arg = 0); //data received - void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout - void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected -#endif - - void ackPacket(struct pbuf * pb);//ack pbuf from onPacket - size_t ack(size_t len); //ack data that you have not acked using the method below - - void ackLater() - { - _ack_pcb = false; //will not ack the current packet. Call from onData - } - - const char * errorToString(int8_t error); - const char * stateToString(); - - //Do not use any of the functions below! - static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb); - static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err); - static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); - static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); - static void _s_error(void *arg, int8_t err); - 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; - } - - //KH - int8_t getClosed_Slot() - { - return _closed_slot; - } - ////// - - protected: - tcp_pcb* _pcb; - std::string _hostname; - int8_t _closed_slot; - -#if ASYNC_TCP_SSL_ENABLED - AcConnectHandlerSSL _connect_cb; - void* _connect_cb_arg; - AcConnectHandlerSSL _discard_cb; - void* _discard_cb_arg; - AcAckHandlerSSL _sent_cb; - void* _sent_cb_arg; - AcErrorHandlerSSL _error_cb; - void* _error_cb_arg; - AcDataHandlerSSL _recv_cb; - void* _recv_cb_arg; - AcPacketHandlerSSL _pb_cb; - void* _pb_cb_arg; - AcTimeoutHandlerSSL _timeout_cb; - void* _timeout_cb_arg; - AcConnectHandlerSSL _poll_cb; - void* _poll_cb_arg; -#else - AcConnectHandler _connect_cb; - void* _connect_cb_arg; - AcConnectHandler _discard_cb; - void* _discard_cb_arg; - AcAckHandler _sent_cb; - void* _sent_cb_arg; - AcErrorHandler _error_cb; - void* _error_cb_arg; - AcDataHandler _recv_cb; - void* _recv_cb_arg; - AcPacketHandler _pb_cb; - void* _pb_cb_arg; - AcTimeoutHandler _timeout_cb; - void* _timeout_cb_arg; - AcConnectHandler _poll_cb; - void* _poll_cb_arg; -#endif - - bool _pcb_busy; - uint32_t _pcb_sent_at; - bool _ack_pcb; - uint32_t _rx_ack_len; - uint32_t _rx_last_packet; - uint32_t _rx_since_timeout; - uint32_t _ack_timeout; - uint16_t _connect_port; - -#if ASYNC_TCP_SSL_ENABLED - size_t _root_ca_len; - char* _root_ca; - size_t _cli_cert_len; - char* _cli_cert; - size_t _cli_key_len; - char* _cli_key; - bool _pcb_secure; - bool _handshake_done; - - const char* _psk_ident; - const char* _psk; -#endif // ASYNC_TCP_SSL_ENABLED - - int8_t _close(); - void _free_closed_slot(); - void _allocate_closed_slot(); - int8_t _connected(void* pcb, int8_t err); - void _error(int8_t err); - int8_t _poll(tcp_pcb* pcb); - int8_t _sent(tcp_pcb* pcb, uint16_t len); - 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: - AsyncSSLClient* prev; - AsyncSSLClient* next; -}; - -#if ASYNC_TCP_SSL_ENABLED -typedef std::function AcSSlFileHandlerSSL; -#endif - -///////////////////////////////////////////////// - -class AsyncSSLServer -{ - public: - AsyncSSLServer(IPAddress addr, uint16_t port); - AsyncSSLServer(uint16_t port); - ~AsyncSSLServer(); - //void onClient(AcConnectHandler cb, void* arg); - -#if ASYNC_TCP_SSL_ENABLED - void onClient(AcConnectHandlerSSL cb, void* arg); - - // Dummy, so it compiles with ESP Async WebServer library enabled. - void onSslFileRequest(AcSSlFileHandlerSSL cb, void* arg) {}; - void beginSecure(const char *cert, const char *private_key_file, const char *password) {}; -#else - void onClient(AcConnectHandler cb, void* arg); -#endif - - void begin(); - void end(); - void setNoDelay(bool nodelay); - bool getNoDelay(); - uint8_t status(); - - //Do not use any of the functions below! - static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err); - static int8_t _s_accepted(void *arg, AsyncSSLClient* client); - - protected: - uint16_t _port; - IPAddress _addr; - bool _noDelay; - - tcp_pcb* _pcb; - -#if ASYNC_TCP_SSL_ENABLED - AcConnectHandlerSSL _connect_cb; -#else - AcConnectHandler _connect_cb; -#endif - - void* _connect_cb_arg; - - int8_t _accept(tcp_pcb* newpcb, int8_t err); - int8_t _accepted(AsyncSSLClient* client); -}; - #include "AsyncTCP_SSL.hpp" +#include "AsyncTCP_SSL_Impl.h" #endif /* ASYNCTCP_SSL_H_ */ diff --git a/src/AsyncTCP_SSL.hpp b/src/AsyncTCP_SSL.hpp index c42d57b..9a44fa3 100644 --- a/src/AsyncTCP_SSL.hpp +++ b/src/AsyncTCP_SSL.hpp @@ -15,12 +15,13 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - Version: 1.1.0 + Version: 1.2.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 21/10/2021 Initial coding to support only ESP32 1.1.0 K Hoang 22/10/2021 Fix bug. Enable coexistence with AsyncTCP + 1.2.0 K Hoang 23/01/2022 Fix `multiple-definitions` linker error *****************************************************************************************************************************/ /* @@ -42,2157 +43,358 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ASYNCTCP_SSL_HPP_ -#define ASYNCTCP_SSL_HPP_ - -#include "Arduino.h" - -#define ASYNC_TCP_SSL_ENABLED true - -//#include "AsyncTCP.h" -#include "AsyncTCP_SSL.h" - -#include "AsyncTCP_SSL_Debug.h" - -extern "C" -{ -#include "lwip/opt.h" -#include "lwip/tcp.h" -#include "lwip/inet.h" -#include "lwip/dns.h" -#include "lwip/err.h" -} - -#include "esp_task_wdt.h" - -#if 0 - #define ASYNC_TCP_SSL_DEBUG(...) do { ets_printf("T %s- ", pcTaskGetTaskName(xTaskGetCurrentTaskHandle())); ets_printf(__VA_ARGS__); } while(0) -#else - #define ASYNC_TCP_SSL_DEBUG(...) -#endif - -/* - TCP/IP Event Task - - This task processes events that correspond to the various callbacks made by LwIP. The callbacks - are handled by _tcp_* functions, which package the info into events, which are processed by this - task. The purpose of this scheme is ??? (to be able to block or spend arbitrary time in the event - handlers without thereby blocking LwIP???). - * */ - -typedef enum -{ - LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_FIN, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR, LWIP_TCP_ACCEPT, LWIP_TCP_CONNECTED, LWIP_TCP_DNS -} lwip_event_t; - -typedef struct -{ - lwip_event_t event; - void *arg; - - 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 - { - AsyncSSLClient * client; - } accept; - - struct - { - const char * name; - ip_addr_t addr; - } dns; - }; -} lwip_event_packet_t; - -static xQueueHandle _async_queue; -static TaskHandle_t _async_service_task_handle = NULL; - - -static SemaphoreHandle_t _slots_lock; -const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; -static int _closed_slots[_number_of_closed_slots]; - -static int _closed_index = []() -{ - _slots_lock = xSemaphoreCreateBinary(); - xSemaphoreGive(_slots_lock); - - for (int i = 0; i < _number_of_closed_slots; ++ i) - { - _closed_slots[i] = 1; - - } - - return 1; -}(); - - -static inline bool _init_async_event_queue() -{ - if (!_async_queue) - { - _async_queue = xQueueCreate(32, 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, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _prepend_async_event(lwip_event_packet_t ** e) -{ - return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _get_async_event(lwip_event_packet_t ** e) -{ - return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static bool _remove_events_with_arg(void * arg) -{ - lwip_event_packet_t * first_packet = NULL; - lwip_event_packet_t * packet = NULL; - - if (!_async_queue) - { - return false; - } - - //figure out which is the first packet so we can keep the order - while (!first_packet) - { - if (xQueueReceive(_async_queue, &first_packet, 0) != pdPASS) - { - return false; - } - - //discard packet if matching - if ((int)first_packet->arg == (int)arg) - { - free(first_packet); - first_packet = NULL; - //return first packet to the back of the queue - } - else if (xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS) - { - return false; - } - } - - while (xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet) - { - if (xQueueReceive(_async_queue, &packet, 0) != pdPASS) - { - return false; - } - - if ((int)packet->arg == (int)arg) - { - free(packet); - packet = NULL; - } - else if (xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS) - { - return false; - } - } - - return true; -} - -static void _handle_async_event(lwip_event_packet_t * e) -{ - ATCP_LOGDEBUG1("_handle_async_event: Task Name = ", pcTaskGetTaskName(xTaskGetCurrentTaskHandle())); - - if (e->event == LWIP_TCP_CLEAR) - { - _remove_events_with_arg(e->arg); - } - else if (e->event == LWIP_TCP_RECV) - { - ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_RECV =", (uint32_t) e->recv.pcb); - AsyncSSLClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err); - } - else if (e->event == LWIP_TCP_FIN) - { - ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_FIN =", (uint32_t) e->fin.pcb); - AsyncSSLClient::_s_fin(e->arg, e->fin.pcb, e->fin.err); - } - else if (e->event == LWIP_TCP_SENT) - { - ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_SENT =", (uint32_t) e->sent.pcb); - AsyncSSLClient::_s_sent(e->arg, e->sent.pcb, e->sent.len); - } - else if (e->event == LWIP_TCP_POLL) - { - ATCP_HEXLOGDEBUG1("_handle_async_event: LWIP_TCP_POLL =", (uint32_t) e->poll.pcb); - AsyncSSLClient::_s_poll(e->arg, e->poll.pcb); - } - else if (e->event == LWIP_TCP_ERROR) - { - ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_ERROR =", (uint32_t) e->arg); - ATCP_LOGINFO1("_handle_async_event: LWIP_TCP_ERROR = ", e->error.err); - AsyncSSLClient::_s_error(e->arg, e->error.err); - } - else if (e->event == LWIP_TCP_CONNECTED) - { - ATCP_HEXLOGINFO2("_handle_async_event: LWIP_TCP_CONNECTED =", (uint32_t) e->arg, (uint32_t) e->connected.pcb); - ATCP_LOGINFO1("_handle_async_event: LWIP_TCP_CONNECTED = ", e->connected.err); - AsyncSSLClient::_s_connected(e->arg, e->connected.pcb, e->connected.err); - } - else if (e->event == LWIP_TCP_ACCEPT) - { - ATCP_HEXLOGINFO2("_handle_async_event: LWIP_TCP_ACCEPT =", (uint32_t) e->arg, (uint32_t) e->accept.client); - AsyncSSLServer::_s_accepted(e->arg, e->accept.client); - } - else if (e->event == LWIP_TCP_DNS) - { - ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_DNS =", (uint32_t) e->arg); - ATCP_LOGINFO3("_handle_async_event: LWIP_TCP_DNS, name =", e->dns.name, ", IP =", ipaddr_ntoa(&e->dns.addr)); - AsyncSSLClient::_s_dns_found(e->dns.name, &e->dns.addr, e->arg); - } - - free((void*)(e)); -} - -static void _async_service_task(void *pvParameters) -{ - lwip_event_packet_t * packet = NULL; - - for (;;) - { - if (_get_async_event(&packet)) - { -#if CONFIG_ASYNC_TCP_USE_WDT - if (esp_task_wdt_add(NULL) != ESP_OK) - { - ATCP_LOGERROR("Failed to add async task to WDT"); - } -#endif - - if (packet) - _handle_async_event(packet); - else - { - ATCP_LOGERROR("_async_service_task, NUL packet"); - } - -#if CONFIG_ASYNC_TCP_USE_WDT - if (esp_task_wdt_delete(NULL) != ESP_OK) - { - ATCP_LOGERROR("Failed to remove loop task from WDT"); - } -#endif - } - } - - vTaskDelete(NULL); - _async_service_task_handle = NULL; -} - -/* - static void _stop_async_task(){ - if(_async_service_task_handle){ - vTaskDelete(_async_service_task_handle); - _async_service_task_handle = NULL; - } - } -*/ - -static bool _start_async_task() -{ - if (!_init_async_event_queue()) - { - return false; - } - - if (!_async_service_task_handle) - { - xTaskCreateUniversal(_async_service_task, "async_tcp_ssl", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE); - - if (!_async_service_task_handle) - { - return false; - } - } - - return true; -} - -/* - LwIP Callbacks - - The following "_tcp_*" functions are called by LwIP on its thread. They all do nothing but - package the callback info into an event, which is queued for the async event task (see above). - * */ - -static int8_t _tcp_clear_events(void * arg) -{ - lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CLEAR; - e->arg = arg; - - if (!_prepend_async_event(&e)) - { - free((void*)(e)); - } - - return ERR_OK; -} - -static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) -{ - ATCP_HEXLOGDEBUG1("_tcp_connected: pcb =", (uint32_t) pcb); - - lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CONNECTED; - e->arg = arg; - e->connected.pcb = pcb; - e->connected.err = err; - - if (!_prepend_async_event(&e)) - { - free((void*)(e)); - } - - return ERR_OK; -} - -static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) -{ - ATCP_HEXLOGDEBUG1("_tcp_poll: pcb =", (uint32_t) pcb); - - lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_POLL; - e->arg = arg; - e->poll.pcb = pcb; - - if (!_send_async_event(&e)) - { - free((void*)(e)); - } - - return ERR_OK; -} - -static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) -{ - lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); - e->arg = arg; - - if (pb) - { - ATCP_HEXLOGDEBUG1("_tcp_recv: pcb =", (uint32_t) pcb); - - e->event = LWIP_TCP_RECV; - e->recv.pcb = pcb; - e->recv.pb = pb; - e->recv.err = err; - } - else - { - ATCP_HEXLOGDEBUG1("_tcp_recv: failed, pcb =", (uint32_t) pcb); - - e->event = LWIP_TCP_FIN; - e->fin.pcb = pcb; - e->fin.err = err; - //close the PCB in LwIP thread - AsyncSSLClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); - } - - if (!_send_async_event(&e)) - { - free((void*)(e)); - } - - return ERR_OK; -} - -static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) -{ - ATCP_HEXLOGDEBUG1("_tcp_sent: pcb =", (uint32_t) pcb); - - lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_SENT; - e->arg = arg; - e->sent.pcb = pcb; - e->sent.len = len; - - if (!_send_async_event(&e)) - { - free((void*)(e)); - } - - return ERR_OK; -} - -static void _tcp_error(void * arg, int8_t err) -{ - ATCP_HEXLOGDEBUG1("_tcp_error: arg =", (uint32_t) arg); - - lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ERROR; - e->arg = arg; - 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) -{ - lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); - - //ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg); - ATCP_LOGDEBUG3("_tcp_dns_found: name =", name, ", IP =", ipaddr_ntoa(ipaddr)); - ATCP_HEXLOGDEBUG1("_tcp_dns_found: arg =", (uint32_t) arg); - - e->event = LWIP_TCP_DNS; - e->arg = arg; - 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)); - } -} - -//Used to switch out from LwIP thread -static int8_t _tcp_accept(void * arg, AsyncSSLClient * client) -{ - lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ACCEPT; - e->arg = arg; - e->accept.client = client; - - if (!_prepend_async_event(&e)) - { - free((void*)(e)); - } - - return ERR_OK; -} - -/* - TCP/IP API Calls - - The following functions provide stubs to call into LwIP's TCP api functions on the LwIP thread - itself. This ensures there are no race conditions between the application and LwIP. - The way it works is that the `_tcp_xxx` functions synchronously call the corresponding - `_tcp_xxx_api` functions on the LwIP thread using a `tcp_api_call` mechanism provided by LwIP. - The `_tcp_xxx_api` function then finally calls the actual `tcp_xxx` function in LwIP and returns - the result. - * */ - -#include "lwip/priv/tcpip_priv.h" - -typedef struct -{ - struct tcpip_api_call_data call; - tcp_pcb * pcb; - int8_t closed_slot; - 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; - - struct - { - ip_addr_t * addr; - uint16_t port; - } bind; - - uint8_t backlog; - }; -} tcp_api_call_t; - -static err_t _tcp_output_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 (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) - { - msg->err = tcp_output(msg->pcb); - } - - return msg->err; -} - -static esp_err_t _tcp_output(tcp_pcb * pcb, int8_t closed_slot) -{ - if (!pcb) - { - return ERR_CONN; - } - - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - 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) -{ - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - - if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) - { - 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, int8_t closed_slot, const char* data, size_t size, uint8_t apiflags) -{ - if (!pcb) - { - return ERR_CONN; - } - - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - 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 (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) - { - msg->err = 0; - tcp_recved(msg->pcb, msg->received); - } - - return msg->err; -} - -static esp_err_t _tcp_recved(tcp_pcb * pcb, int8_t closed_slot, size_t len) -{ - if (!pcb) - { - return ERR_CONN; - } - - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - 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) -{ - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - - if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) - { - msg->err = tcp_close(msg->pcb); - } - - return msg->err; -} - -static esp_err_t _tcp_close(tcp_pcb * pcb, int8_t closed_slot) -{ - if (!pcb) - { - return ERR_CONN; - } - - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - 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) -{ - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - - if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) - { - tcp_abort(msg->pcb); - } - - return msg->err; -} - -static esp_err_t _tcp_abort(tcp_pcb * pcb, int8_t closed_slot) -{ - if (!pcb) - { - return ERR_CONN; - } - - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - 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) -{ - tcp_api_call_t * 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, int8_t closed_slot, 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.closed_slot = closed_slot; - 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; -} - -static err_t _tcp_bind_api(struct tcpip_api_call_data *api_call_msg) -{ - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); - - return msg->err; -} - -static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) -{ - if (!pcb) - { - return ESP_FAIL; - } - - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = -1; - msg.bind.addr = addr; - msg.bind.port = port; - tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg); - - return msg.err; -} - -static err_t _tcp_listen_api(struct tcpip_api_call_data *api_call_msg) -{ - tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = 0; - msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); - - return msg->err; -} - -static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) -{ - if (!pcb) - { - return NULL; - } - - tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = -1; - msg.backlog = backlog ? backlog : 0xFF; - tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg); - - return msg.pcb; -} - -// KH -#if ASYNC_TCP_SSL_ENABLED -//#if 1 //ASYNC_TCP_SSL_ENABLED -extern "C" -{ - // The following API stubs are for use in tcp_mbedtls.c - // They are callable from C and take a void* instead of an AsyncSSLClient*. - - esp_err_t _tcp_output4ssl(tcp_pcb * pcb, void* client) - { - // KH - //return _tcp_output(pcb, reinterpret_cast (client) ); - return _tcp_output(pcb, (reinterpret_cast (client) )->getClosed_Slot() ); - ////// - } - - esp_err_t _tcp_write4ssl(tcp_pcb * pcb, const char* data, size_t size, uint8_t apiflags, void* client) - { - // KH - //return _tcp_write(pcb, data, size, apiflags, reinterpret_cast (client) ); - // static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char* data, size_t size, uint8_t apiflags) - // _tcp_write(_pcb, _closed_slot, data, will_send, apiflags); - //return _tcp_write(pcb, _closed_slots, data, size, apiflags, (int8_t) (reinterpret_cast (client) ) ); - return _tcp_write(pcb, (reinterpret_cast (client) )->getClosed_Slot(), data, size, apiflags); - ////// - } - -} -#endif - -/* - Async TCP Client -*/ - -AsyncSSLClient::AsyncSSLClient(tcp_pcb* pcb) - : _connect_cb(0) - , _connect_cb_arg(0) - , _discard_cb(0) - , _discard_cb_arg(0) - , _sent_cb(0) - , _sent_cb_arg(0) - , _error_cb(0) - , _error_cb_arg(0) - , _recv_cb(0) - , _recv_cb_arg(0) - , _pb_cb(0) - , _pb_cb_arg(0) - , _timeout_cb(0) - , _timeout_cb_arg(0) - , _pcb_busy(false) - , _pcb_sent_at(0) - , _ack_pcb(true) - , _rx_last_packet(0) - , _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) - , _cli_cert_len(0) - , _cli_cert(NULL) - , _cli_key_len(0) - , _cli_key(NULL) - , _pcb_secure(false) - , _handshake_done(true) - , _psk_ident(0) - , _psk(0) -#endif // ASYNC_TCP_SSL_ENABLED - , prev(NULL) - , next(NULL) -{ - _pcb = pcb; - _closed_slot = -1; - if (_pcb) { - _allocate_closed_slot(); - _rx_last_packet = millis(); - tcp_arg(_pcb, this); - tcp_recv(_pcb, &_tcp_recv); - tcp_sent(_pcb, &_tcp_sent); - tcp_err(_pcb, &_tcp_error); - tcp_poll(_pcb, &_tcp_poll, 1); - } -} - -AsyncSSLClient::~AsyncSSLClient() -{ - if (_pcb) - { - _close(); - } - - _free_closed_slot(); -} - -/* - Operators - * */ - -AsyncSSLClient& AsyncSSLClient::operator=(const AsyncSSLClient& other) -{ - if (_pcb) - { - _close(); - } - - _pcb = other._pcb; - _closed_slot = other._closed_slot; - - if (_pcb) - { - _rx_last_packet = millis(); - tcp_arg(_pcb, this); - tcp_recv(_pcb, &_tcp_recv); - 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; -} - -bool AsyncSSLClient::operator==(const AsyncSSLClient &other) -{ - return _pcb == other._pcb; -} - -AsyncSSLClient & AsyncSSLClient::operator+=(const AsyncSSLClient &other) -{ - if (next == NULL) - { - next = (AsyncSSLClient*)(&other); - next->prev = this; - } - else - { - AsyncSSLClient *c = next; - - while (c->next != NULL) - { - c = c->next; - } - - c->next = (AsyncSSLClient*)(&other); - c->next->prev = c; - } - - return *this; -} - -/* - Callback Setters - * */ - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::onConnect(AcConnectHandlerSSL cb, void* arg) -#else -void AsyncSSLClient::onConnect(AcConnectHandler cb, void* arg) -#endif -{ - _connect_cb = cb; - _connect_cb_arg = arg; -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::onDisconnect(AcConnectHandlerSSL cb, void* arg) -#else -void AsyncSSLClient::onDisconnect(AcConnectHandler cb, void* arg) -#endif -{ - _discard_cb = cb; - _discard_cb_arg = arg; -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::onAck(AcAckHandlerSSL cb, void* arg) -#else -void AsyncSSLClient::onAck(AcAckHandler cb, void* arg) -#endif -{ - _sent_cb = cb; - _sent_cb_arg = arg; -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::onError(AcErrorHandlerSSL cb, void* arg) -#else -void AsyncSSLClient::onError(AcErrorHandler cb, void* arg) -#endif -{ - _error_cb = cb; - _error_cb_arg = arg; -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::onData(AcDataHandlerSSL cb, void* arg) -#else -void AsyncSSLClient::onData(AcDataHandler cb, void* arg) -#endif -{ - _recv_cb = cb; - _recv_cb_arg = arg; -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::onPacket(AcPacketHandlerSSL cb, void* arg) -#else -void AsyncSSLClient::onPacket(AcPacketHandler cb, void* arg) -#endif -{ - _pb_cb = cb; - _pb_cb_arg = arg; -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::onTimeout(AcTimeoutHandlerSSL cb, void* arg) -#else -void AsyncSSLClient::onTimeout(AcTimeoutHandler cb, void* arg) -#endif -{ - _timeout_cb = cb; - _timeout_cb_arg = arg; -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::onPoll(AcConnectHandlerSSL cb, void* arg) -#else -void AsyncSSLClient::onPoll(AcConnectHandler cb, void* arg) -#endif -{ - _poll_cb = cb; - _poll_cb_arg = arg; -} - -/* - Main Public Methods - * */ - -#if ASYNC_TCP_SSL_ENABLED -bool AsyncSSLClient::connect(IPAddress ip, uint16_t port, bool secure) -{ -#else -bool AsyncSSLClient::connect(IPAddress ip, uint16_t port) -{ -#endif // ASYNC_TCP_SSL_ENABLED - if (_pcb) - { - ATCP_LOGWARN1("connect: already connected, state =", _pcb->state); - return false; - } - - if (!_start_async_task()) - { - ATCP_LOGERROR("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) - { - ATCP_LOGERROR("connect: NULL pcb"); - - 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); - tcp_sent(pcb, &_tcp_sent); - tcp_poll(pcb, &_tcp_poll, 1); - //_tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected); - _tcp_connect(pcb, _closed_slot, &addr, port, (tcp_connected_fn)&_tcp_connected); - - return true; -} - -#if ASYNC_TCP_SSL_ENABLED -bool AsyncSSLClient::connect(const char* host, uint16_t port, bool secure) -#else -bool AsyncSSLClient::connect(const char* host, uint16_t port) -#endif // ASYNC_TCP_SSL_ENABLED -{ - ip_addr_t addr; - - if (!_start_async_task()) - { - ATCP_LOGERROR("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) - { -#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; - } - - ATCP_LOGERROR1("connect: error =", err); - - return false; -} - -void AsyncSSLClient::close(bool now) -{ - if (_pcb) - { - _tcp_recved(_pcb, _closed_slot, _rx_ack_len); - } - - _close(); -} - -int8_t AsyncSSLClient::abort() -{ - if (_pcb) - { - _tcp_abort(_pcb, _closed_slot ); - _pcb = NULL; - } - - return ERR_ABRT; -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::setRootCa(const char* rootca, const size_t len) -{ - _root_ca = (char*)rootca; - _root_ca_len = len; -} - -void AsyncSSLClient::setClientCert(const char* cli_cert, const size_t len) -{ - _cli_cert = (char*)cli_cert; - _cli_cert_len = len; -} - -void AsyncSSLClient::setClientKey(const char* cli_key, const size_t len) -{ - _cli_key = (char*)cli_key; - _cli_key_len = len; -} - -void AsyncSSLClient::setPsk(const char* psk_ident, const char* psk) -{ - _psk_ident = psk_ident; - _psk = psk; -} -#endif // ASYNC_TCP_SSL_ENABLED - -size_t AsyncSSLClient::space() -{ - if ((_pcb != NULL) && (_pcb->state == 4)) - { - return tcp_sndbuf(_pcb); - } - - return 0; -} - -size_t AsyncSSLClient::add(const char* data, size_t size, uint8_t apiflags) -{ - if (!_pcb || size == 0 || data == NULL) - { - return 0; - } - - size_t room = space(); - - if (!room) - { - return 0; - } - -#if ASYNC_TCP_SSL_ENABLED - if (_pcb_secure) - { - int sent = tcp_ssl_write(_pcb, (uint8_t*)data, size); - - ASYNC_TCP_SSL_DEBUG("add() tcp_ssl_write size = %d\n", sent); - - if (sent >= 0) - { - // @ToDo: ??? - //_tx_unacked_len += sent; - return sent; - } - - ATCP_LOGINFO1("add() done, tcp_ssl_write size =", 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, _closed_slot, data, will_send, apiflags); - - if (err != ERR_OK) - { - return 0; - } - - return will_send; -} - -bool AsyncSSLClient::send() -{ -#if ASYNC_TCP_SSL_ENABLED - - //delay(1); // 5 is OK - vTaskDelay(1 / portTICK_PERIOD_MS); - - //if(_pcb_secure) return true; -#endif // ASYNC_TCP_SSL_ENABLED - int8_t err = ERR_OK; - err = _tcp_output(_pcb, _closed_slot); - - if (err == ERR_OK) - { - _pcb_busy = true; - _pcb_sent_at = millis(); - return true; - } - - return false; -} - -size_t AsyncSSLClient::ack(size_t len) -{ - if (len > _rx_ack_len) - len = _rx_ack_len; - - if (len) - { - _tcp_recved(_pcb, _closed_slot, len); - } - - _rx_ack_len -= len; - - return len; -} - -void AsyncSSLClient::ackPacket(struct pbuf * pb) -{ - if (!pb) - { - return; - } - - _tcp_recved(_pcb, _closed_slot, pb->len); - pbuf_free(pb); -} - -/* - Main Private Methods - * */ - -int8_t AsyncSSLClient::_close() -{ - //ets_printf("X: 0x%08x\n", (uint32_t)this); - int8_t err = ERR_OK; - - 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); - tcp_err(_pcb, NULL); - tcp_poll(_pcb, NULL, 0); - _tcp_clear_events(this); - err = _tcp_close(_pcb, _closed_slot); - - if (err != ERR_OK) - { - err = abort(); - } - - _pcb = NULL; - - if (_discard_cb) - { - _discard_cb(_discard_cb_arg, this); - } - } - - return err; -} - -void AsyncSSLClient::_allocate_closed_slot() -{ - xSemaphoreTake(_slots_lock, portMAX_DELAY); - uint32_t closed_slot_min_index = 0; - - for (int i = 0; i < _number_of_closed_slots; ++ i) - { - if ((_closed_slot == -1 || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) - { - closed_slot_min_index = _closed_slots[i]; - _closed_slot = i; - } - } - - if (_closed_slot != -1) - { - _closed_slots[_closed_slot] = 0; - } - - xSemaphoreGive(_slots_lock); -} - -void AsyncSSLClient::_free_closed_slot() -{ - if (_closed_slot != -1) - { - _closed_slots[_closed_slot] = _closed_index; - _closed_slot = -1; - ++ _closed_index; - } -} - -/* - Private Callbacks - * */ - -int8_t AsyncSSLClient::_connected(void* pcb, int8_t err) -{ - _pcb = reinterpret_cast(pcb); - - if (_pcb) - { - _rx_last_packet = millis(); - _pcb_busy = false; - // 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 (_psk_ident != NULL and _psk != NULL) - { - err = tcp_ssl_new_psk_client(_pcb, this, _psk_ident, _psk) < 0; - } - else - { - err = tcp_ssl_new_client(_pcb, this, _hostname.empty() ? NULL : _hostname.c_str(), - _root_ca, _root_ca_len, _cli_cert, _cli_cert_len, _cli_key, _cli_key_len) < 0; - } - - if (err) - { - ATCP_LOGERROR("_connected: error => closing"); - - return _close(); - } - - 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 AsyncSSLClient::_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); - - if (_pcb->state == LISTEN) - { - tcp_sent(_pcb, NULL); - tcp_recv(_pcb, NULL); - tcp_err(_pcb, NULL); - tcp_poll(_pcb, NULL, 0); - } - _pcb = NULL; - } - - if (_error_cb) - { - _error_cb(_error_cb_arg, this, err); - } - - if (_discard_cb) - { - _discard_cb(_discard_cb_arg, this); - } -} - -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::_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 AsyncSSLClient::_lwip_fin(tcp_pcb* pcb, int8_t err) -{ - if (!_pcb || pcb != _pcb) - { - ATCP_HEXLOGDEBUG2("_lwip_fin: pcb/_pcb =", (uint32_t)pcb, (uint32_t)_pcb); - - return ERR_OK; - } +#ifndef ASYNCTCP_SSL_HPP +#define ASYNCTCP_SSL_HPP - tcp_arg(_pcb, NULL); +#if !(ESP32) + #error This AsyncTCP_SSL library is supporting only ESP32 +#endif - if (_pcb->state == LISTEN) - { - tcp_sent(_pcb, NULL); - tcp_recv(_pcb, NULL); - tcp_err(_pcb, NULL); - tcp_poll(_pcb, NULL, 0); - } +///////////////////////////////////////////////// - if (tcp_close(_pcb) != ERR_OK) - { - tcp_abort(_pcb); - } +#define ASYNC_TCP_SSL_VERSION "AsyncTCP_SSL v1.2.0" - _free_closed_slot(); - _pcb = NULL; +#define ASYNC_TCP_SSL_VERSION_MAJOR 1 +#define ASYNC_TCP_SSL_VERSION_MINOR 2 +#define ASYNC_TCP_SSL_VERSION_PATCH 0 - return ERR_OK; -} +#define ASYNC_TCP_SSL_VERSION_INT 1002000 -//In Async Thread -int8_t AsyncSSLClient::_fin(tcp_pcb* pcb, int8_t err) -{ - _tcp_clear_events(this); +///////////////////////////////////////////////// - if (_discard_cb) - { - _discard_cb(_discard_cb_arg, this); - } +#define ASYNC_TCP_SSL_ENABLED true - return ERR_OK; -} +#include "Arduino.h" -int8_t AsyncSSLClient::_sent(tcp_pcb* pcb, uint16_t len) -{ - _rx_last_packet = millis(); - - ATCP_LOGINFO1("_sent: len =", len); - - _pcb_busy = false; +#include "IPAddress.h" +#include "sdkconfig.h" +#include +#include - if (_sent_cb) - { - _sent_cb(_sent_cb_arg, this, len, (millis() - _pcb_sent_at)); - } +// in ./libraries/WiFiClientSecure/src/ssl_client.h for ESP32 +#include - return ERR_OK; -} +#include "tcp_mbedtls.h" -int8_t AsyncSSLClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) +extern "C" { - while (pb != NULL) - { - _rx_last_packet = millis(); - pbuf *nxt = pb->next; - pb->next = NULL; - -#if ASYNC_TCP_SSL_ENABLED - if (_pcb_secure) - { - ATCP_LOGINFO1("_recv: tot_len =", pb->tot_len); - - int err = tcp_ssl_read(pcb, pb); - // tcp_ssl_read always processes the full pbuf, so ack all of it - // KH - //_tcp_recved(pcb, pb->len); - _tcp_recved(pcb, _closed_slot, pb->len); - ////// - pbuf_free(pb); - // handle errors - if (err < 0) - { - if (err != MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) - { - ATCP_LOGERROR1("_recv: err =", err); - - _close(); - } - - return ERR_BUF; // for lack of a better error value - } - - } else -#endif // ASYNC_TCP_SSL_ENABLED - { - //we should not ack before we assimilate the data - _ack_pcb = true; - - if (_pb_cb) - { - _pb_cb(_pb_cb_arg, this, pb); - } - else - { - if (_recv_cb) - { - _recv_cb(_recv_cb_arg, this, pb->payload, pb->len); - } - - if (!_ack_pcb) - { - _rx_ack_len += pb->len; - } - else if (_pcb) - { - _tcp_recved(_pcb, _closed_slot, pb->len); - } - pbuf_free(pb); - } - } - - pb = nxt; - } - - return ERR_OK; + #include "freertos/semphr.h" + #include "lwip/pbuf.h" } -int8_t AsyncSSLClient::_poll(tcp_pcb* pcb) -{ - if (!_pcb) - { - ATCP_LOGWARN("_poll: NULL pcb"); - - return ERR_OK; - } - - if (pcb != _pcb) - { - ATCP_HEXLOGERROR2("_poll: diff pcb/_pcb =", (uint32_t) pcb, (uint32_t) _pcb); - - return ERR_OK; - } - - uint32_t now = millis(); - - // ACK Timeout - if (_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout) - { - _pcb_busy = false; - - ATCP_LOGWARN1("_poll: ack timeout, state =", pcb->state); - - if (_timeout_cb) - _timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at)); +#include "AsyncTCP_SSL_Debug.h" - return ERR_OK; - } +////////////////////////////////////////////////////////////////////////////////////////// - // RX Timeout - if (_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)) - { - ATCP_LOGWARN1("_poll: rx timeout, state =", pcb->state); - - _close(); - return ERR_OK; - } +//If core is not defined, then we are running in Arduino or PIO +#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE + #define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core + #define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event +#endif -#if ASYNC_TCP_SSL_ENABLED - if (_pcb_secure && !_handshake_done && (now - _rx_last_packet) >= SSL_HANDSHAKE_TIMEOUT) - { - ATCP_LOGWARN1("_poll: ssl handshake timeout, state =", pcb->state); - - _close(); - return ERR_OK; - } -#endif // ASYNC_TCP_SSL_ENABLED +class AsyncSSLClient; - // Everything is fine - if (_poll_cb) - { - _poll_cb(_poll_cb_arg, this); - } +#define ASYNC_MAX_ACK_TIME 5000 +#define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given) +#define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react. +#define SSL_HANDSHAKE_TIMEOUT 5000 // timeout to complete SSL handshake - return ERR_OK; -} +////////////////////////////////////////////////////////////////////////////////////////////// -void AsyncSSLClient::_dns_found(struct ip_addr *ipaddr) -{ - if (ipaddr && ipaddr->u_addr.ip4.addr) - { #if ASYNC_TCP_SSL_ENABLED - connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port, _pcb_secure); +typedef std::function AcConnectHandlerSSL; +typedef std::function AcAckHandlerSSL; +typedef std::function AcErrorHandlerSSL; +typedef std::function AcDataHandlerSSL; +typedef std::function AcPacketHandlerSSL; +typedef std::function AcTimeoutHandlerSSL; #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); - } - - if (_discard_cb) - { - _discard_cb(_discard_cb_arg, this); - } - } -} - -/* - Public Helper Methods - * */ - -void AsyncSSLClient::stop() -{ - close(false); -} - -bool AsyncSSLClient::free() -{ - if (!_pcb) - { - return true; - } - - if (_pcb->state == 0 || _pcb->state > 4) - { - return true; - } - - return false; -} - -size_t AsyncSSLClient::write(const char* data) -{ - if (data == NULL) - { - 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 IPAddress(getRemoteAddress()); -} - -uint16_t AsyncSSLClient::remotePort() -{ - return getRemotePort(); -} - -IPAddress AsyncSSLClient::localIP() -{ - return IPAddress(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; - } - - return _pcb->state == 4; -} - -bool AsyncSSLClient::connecting() -{ - if (!_pcb) - { - return false; - } - - return _pcb->state > 0 && _pcb->state < 4; -} - -bool AsyncSSLClient::disconnecting() -{ - if (!_pcb) - { - return false; - } - - return _pcb->state > 4 && _pcb->state < 10; -} - -bool AsyncSSLClient::disconnected() -{ - if (!_pcb) - { - return true; - } - - return _pcb->state == 0 || _pcb->state == 10; -} - -bool AsyncSSLClient::freeable() -{ - if (!_pcb) - { - return true; - } - - return _pcb->state == 0 || _pcb->state > 4; -} - -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_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 -55: return "DNS failed"; - default: return "UNKNOWN"; - } -} - -const char * AsyncSSLClient::stateToString() -{ - switch (state()) - { - case 0: return "Closed"; - case 1: return "Listen"; - case 2: return "SYN Sent"; - case 3: return "SYN Received"; - case 4: return "Established"; - case 5: return "FIN Wait 1"; - case 6: return "FIN Wait 2"; - case 7: return "Close Wait"; - case 8: return "Closing"; - case 9: return "Last ACK"; - case 10: return "Time Wait"; - default: return "UNKNOWN"; - } -} +typedef std::function AcConnectHandler; +typedef std::function AcAckHandler; +typedef std::function AcErrorHandler; +typedef std::function AcDataHandler; +typedef std::function AcPacketHandler; +typedef std::function AcTimeoutHandler; +#endif -/* - Static Callbacks (LwIP C2C++ interconnect) - * */ +///////////////////////////////////////////////// -void AsyncSSLClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) -{ - reinterpret_cast(arg)->_dns_found(ipaddr); -} +struct tcp_pcb; +struct ip_addr; -int8_t AsyncSSLClient::_s_poll(void * arg, struct tcp_pcb * pcb) -{ - return reinterpret_cast(arg)->_poll(pcb); -} +////////////////////////////////////////////////////////////////////////////////////////////// -int8_t AsyncSSLClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) +class AsyncSSLClient { - return reinterpret_cast(arg)->_recv(pcb, pb, err); -} + public: + AsyncSSLClient(tcp_pcb* pcb = 0); + ~AsyncSSLClient(); -int8_t AsyncSSLClient::_s_fin(void * arg, struct tcp_pcb * pcb, int8_t err) -{ - return reinterpret_cast(arg)->_fin(pcb, err); -} + AsyncSSLClient & operator=(const AsyncSSLClient &other); + AsyncSSLClient & operator+=(const AsyncSSLClient &other); -int8_t AsyncSSLClient::_s_lwip_fin(void * arg, struct tcp_pcb * pcb, int8_t err) -{ - return reinterpret_cast(arg)->_lwip_fin(pcb, err); -} + bool operator==(const AsyncSSLClient &other); -int8_t AsyncSSLClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) -{ - return reinterpret_cast(arg)->_sent(pcb, len); -} + bool operator!=(const AsyncSSLClient &other) + { + return !(*this == other); + } -void AsyncSSLClient::_s_error(void * arg, int8_t err) -{ - reinterpret_cast(arg)->_error(err); -} +#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 setClientCert(const char* cli_cert, const size_t len); + void setClientKey(const char* cli_key, 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 -int8_t AsyncSSLClient::_s_connected(void * arg, void * pcb, int8_t err) -{ - return reinterpret_cast(arg)->_connected(pcb, err); -} + void close(bool now = false); + void stop(); + int8_t abort(); + bool free(); -#if ASYNC_TCP_SSL_ENABLED -void AsyncSSLClient::_s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len) -{ - AsyncSSLClient *c = reinterpret_cast(arg); + bool canSend();//ack is not pending + size_t space();//space available in the TCP window + size_t add(const char* data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); //add for sending + bool send();//send all data added with the method above - if (c->_recv_cb) - c->_recv_cb(c->_recv_cb_arg, c, data, len); -} + //write equals add()+send() + size_t write(const char* data); + size_t write(const char* data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); //only when canSend() == true -void AsyncSSLClient::_s_handshake(void *arg, struct tcp_pcb *tcp, struct tcp_ssl_pcb* ssl) -{ - AsyncSSLClient *c = reinterpret_cast(arg); - c->_handshake_done = true; + uint8_t state(); + bool connecting(); + bool connected(); + bool disconnecting(); + bool disconnected(); + bool freeable();//disconnected or disconnecting - if (c->_connect_cb) - c->_connect_cb(c->_connect_cb_arg, c); -} + uint16_t getMss(); -void AsyncSSLClient::_s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err) -{ - reinterpret_cast(arg)->_ssl_error(err); -} -#endif // ASYNC_TCP_SSL_ENABLED + uint32_t getRxTimeout(); + void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds -/* - Async TCP Server -*/ + uint32_t getAckTimeout(); + void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds -AsyncSSLServer::AsyncSSLServer(IPAddress addr, uint16_t port) - : _port(port) - , _addr(addr) - , _noDelay(false) - , _pcb(0) - , _connect_cb(0) - , _connect_cb_arg(0) -{} + void setNoDelay(bool nodelay); + bool getNoDelay(); -AsyncSSLServer::AsyncSSLServer(uint16_t port) - : _port(port) - , _addr((uint32_t) IPADDR_ANY) - , _noDelay(false) - , _pcb(0) - , _connect_cb(0) - , _connect_cb_arg(0) -{} + uint32_t getRemoteAddress(); + uint16_t getRemotePort(); + uint32_t getLocalAddress(); + uint16_t getLocalPort(); -AsyncSSLServer::~AsyncSSLServer() -{ - end(); -} + //compatibility + IPAddress remoteIP(); + uint16_t remotePort(); + IPAddress localIP(); + uint16_t localPort(); #if ASYNC_TCP_SSL_ENABLED -void AsyncSSLServer::onClient(AcConnectHandlerSSL cb, void* arg) + void onConnect(AcConnectHandlerSSL cb, void* arg = 0); //on successful connect + void onDisconnect(AcConnectHandlerSSL cb, void* arg = 0); //disconnected + void onAck(AcAckHandlerSSL cb, void* arg = 0); //ack received + void onError(AcErrorHandlerSSL cb, void* arg = 0); //unsuccessful connect or error + void onData(AcDataHandlerSSL cb, void* arg = 0); //data received (called if onPacket is not used) + void onPacket(AcPacketHandlerSSL cb, void* arg = 0); //data received + void onTimeout(AcTimeoutHandlerSSL cb, void* arg = 0); //ack timeout + void onPoll(AcConnectHandlerSSL cb, void* arg = 0); //every 125ms when connected #else -void AsyncSSLServer::onClient(AcConnectHandler cb, void* arg) + void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect + void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected + void onAck(AcAckHandler cb, void* arg = 0); //ack received + void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error + void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used) + void onPacket(AcPacketHandler cb, void* arg = 0); //data received + void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout + void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected #endif -{ - _connect_cb = cb; - _connect_cb_arg = arg; -} - -void AsyncSSLServer::begin() -{ - if (_pcb) - { - return; - } - - if (!_start_async_task()) - { - ATCP_LOGERROR("begin: failed to start task"); - - return; - } - - int8_t err; - _pcb = tcp_new_ip_type(IPADDR_TYPE_V4); - if (!_pcb) - { - ATCP_LOGERROR("begin: NULL pcb"); + void ackPacket(struct pbuf * pb);//ack pbuf from onPacket + size_t ack(size_t len); //ack data that you have not acked using the method below - return; - } - - ip_addr_t local_addr; - local_addr.type = IPADDR_TYPE_V4; - local_addr.u_addr.ip4.addr = (uint32_t) _addr; - err = _tcp_bind(_pcb, &local_addr, _port); + void ackLater() + { + _ack_pcb = false; //will not ack the current packet. Call from onData + } - if (err != ERR_OK) - { - _tcp_close(_pcb, -1); + const char * errorToString(int8_t error); + const char * stateToString(); + + //Do not use any of the functions below! + static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb); + static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err); + static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); + static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); + static void _s_error(void *arg, int8_t err); + 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); - ATCP_LOGERROR1("begin: bind error, err =", err); - - return; - } - - static uint8_t backlog = 5; - _pcb = _tcp_listen_with_backlog(_pcb, backlog); +#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 - if (!_pcb) - { - ATCP_LOGERROR("begin: NULL listen_pcb"); + int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err); - return; - } - - tcp_arg(_pcb, (void*) this); - tcp_accept(_pcb, &_s_accept); -} - -void AsyncSSLServer::end() -{ - if (_pcb) - { - tcp_arg(_pcb, NULL); - tcp_accept(_pcb, NULL); + tcp_pcb * pcb() + { + return _pcb; + } - if (tcp_close(_pcb) != ERR_OK) + //KH + int8_t getClosed_Slot() { - _tcp_abort(_pcb, -1); + return _closed_slot; } + ////// - _pcb = NULL; - } -} + protected: + tcp_pcb* _pcb; + std::string _hostname; + int8_t _closed_slot; -//runs on LwIP thread -int8_t AsyncSSLServer::_accept(tcp_pcb* pcb, int8_t err) -{ - ATCP_HEXLOGDEBUG1("_accept: pcb =", (uint32_t) pcb); - - if (_connect_cb) - { - AsyncSSLClient *c = new AsyncSSLClient(pcb); +#if ASYNC_TCP_SSL_ENABLED + AcConnectHandlerSSL _connect_cb; + void* _connect_cb_arg; + AcConnectHandlerSSL _discard_cb; + void* _discard_cb_arg; + AcAckHandlerSSL _sent_cb; + void* _sent_cb_arg; + AcErrorHandlerSSL _error_cb; + void* _error_cb_arg; + AcDataHandlerSSL _recv_cb; + void* _recv_cb_arg; + AcPacketHandlerSSL _pb_cb; + void* _pb_cb_arg; + AcTimeoutHandlerSSL _timeout_cb; + void* _timeout_cb_arg; + AcConnectHandlerSSL _poll_cb; + void* _poll_cb_arg; +#else + AcConnectHandler _connect_cb; + void* _connect_cb_arg; + AcConnectHandler _discard_cb; + void* _discard_cb_arg; + AcAckHandler _sent_cb; + void* _sent_cb_arg; + AcErrorHandler _error_cb; + void* _error_cb_arg; + AcDataHandler _recv_cb; + void* _recv_cb_arg; + AcPacketHandler _pb_cb; + void* _pb_cb_arg; + AcTimeoutHandler _timeout_cb; + void* _timeout_cb_arg; + AcConnectHandler _poll_cb; + void* _poll_cb_arg; +#endif - if (c) - { - c->setNoDelay(_noDelay); - return _tcp_accept(this, c); - } - } + bool _pcb_busy; + uint32_t _pcb_sent_at; + bool _ack_pcb; + uint32_t _rx_ack_len; + uint32_t _rx_last_packet; + uint32_t _rx_since_timeout; + uint32_t _ack_timeout; + uint16_t _connect_port; - if (tcp_close(pcb) != ERR_OK) - { - tcp_abort(pcb); - } +#if ASYNC_TCP_SSL_ENABLED + size_t _root_ca_len; + char* _root_ca; + size_t _cli_cert_len; + char* _cli_cert; + size_t _cli_key_len; + char* _cli_key; + bool _pcb_secure; + bool _handshake_done; + + const char* _psk_ident; + const char* _psk; +#endif // ASYNC_TCP_SSL_ENABLED - ATCP_LOGERROR("_accept: fail"); + int8_t _close(); + void _free_closed_slot(); + void _allocate_closed_slot(); + int8_t _connected(void* pcb, int8_t err); + void _error(int8_t err); + int8_t _poll(tcp_pcb* pcb); + int8_t _sent(tcp_pcb* pcb, uint16_t len); + 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 - return ERR_OK; -} + public: + AsyncSSLClient* prev; + AsyncSSLClient* next; +}; -int8_t AsyncSSLServer::_accepted(AsyncSSLClient* client) -{ - if (_connect_cb) - { - _connect_cb(_connect_cb_arg, client); - } +#if ASYNC_TCP_SSL_ENABLED +typedef std::function AcSSlFileHandlerSSL; +#endif - return ERR_OK; -} +////////////////////////////////////////////////////////////////////////////////////////////// -void AsyncSSLServer::setNoDelay(bool nodelay) +class AsyncSSLServer { - _noDelay = nodelay; -} + public: + AsyncSSLServer(IPAddress addr, uint16_t port); + AsyncSSLServer(uint16_t port); + ~AsyncSSLServer(); + //void onClient(AcConnectHandler cb, void* arg); + +#if ASYNC_TCP_SSL_ENABLED + void onClient(AcConnectHandlerSSL cb, void* arg); + + // Dummy, so it compiles with ESP Async WebServer library enabled. + void onSslFileRequest(AcSSlFileHandlerSSL cb, void* arg) {}; + void beginSecure(const char *cert, const char *private_key_file, const char *password) {}; +#else + void onClient(AcConnectHandler cb, void* arg); +#endif -bool AsyncSSLServer::getNoDelay() -{ - return _noDelay; -} + void begin(); + void end(); + void setNoDelay(bool nodelay); + bool getNoDelay(); + uint8_t status(); -uint8_t AsyncSSLServer::status() -{ - if (!_pcb) - { - return 0; - } + //Do not use any of the functions below! + static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err); + static int8_t _s_accepted(void *arg, AsyncSSLClient* client); - return _pcb->state; -} + protected: + uint16_t _port; + IPAddress _addr; + bool _noDelay; + + tcp_pcb* _pcb; + +#if ASYNC_TCP_SSL_ENABLED + AcConnectHandlerSSL _connect_cb; +#else + AcConnectHandler _connect_cb; +#endif + + void* _connect_cb_arg; -int8_t AsyncSSLServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err) -{ - return reinterpret_cast(arg)->_accept(pcb, err); -} + int8_t _accept(tcp_pcb* newpcb, int8_t err); + int8_t _accepted(AsyncSSLClient* client); +}; -int8_t AsyncSSLServer::_s_accepted(void *arg, AsyncSSLClient* client) -{ - return reinterpret_cast(arg)->_accepted(client); -} +////////////////////////////////////////////////////////////////////////////////////////////// -#endif /* ASYNCTCP_SSL_HPP_ */ +#endif /* ASYNCTCP_SSL_HPP */ diff --git a/src/AsyncTCP_SSL_Debug.h b/src/AsyncTCP_SSL_Debug.h index a8dc87a..fdebfc2 100644 --- a/src/AsyncTCP_SSL_Debug.h +++ b/src/AsyncTCP_SSL_Debug.h @@ -15,12 +15,13 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - Version: 1.1.0 + Version: 1.2.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 21/10/2021 Initial coding to support only ESP32 1.1.0 K Hoang 22/10/2021 Fix bug. Enable coexistence with AsyncTCP + 1.2.0 K Hoang 23/01/2022 Fix `multiple-definitions` linker error *****************************************************************************************************************************/ #pragma once diff --git a/src/AsyncTCP_SSL_Impl.h b/src/AsyncTCP_SSL_Impl.h new file mode 100644 index 0000000..4cfa49f --- /dev/null +++ b/src/AsyncTCP_SSL_Impl.h @@ -0,0 +1,2467 @@ +/**************************************************************************************************************************** + AsyncTCP_SSL_Impl.h + + AsyncTCP_SSL is a library for ESP32 + + Based on and modified from : + + 1) AsyncTCP (https://github.com/me-no-dev/ESPAsyncTCP) + 2) AsyncTCP (https://github.com/tve/AsyncTCP) + + Built by Khoi Hoang https://github.com/khoih-prog/AsyncTCP_SSL + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + as published bythe Free Software Foundation, either version 3 of the License, or (at your option) any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + Version: 1.2.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 21/10/2021 Initial coding to support only ESP32 + 1.1.0 K Hoang 22/10/2021 Fix bug. Enable coexistence with AsyncTCP + 1.2.0 K Hoang 23/01/2022 Fix `multiple-definitions` linker error + *****************************************************************************************************************************/ + +/* + Asynchronous TCP library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ASYNCTCP_SSL_IML_H +#define ASYNCTCP_SSL_IML_H + +#include "Arduino.h" + +#define ASYNC_TCP_SSL_ENABLED true + +#include "AsyncTCP_SSL_Debug.h" + +extern "C" +{ + #include "lwip/opt.h" + #include "lwip/tcp.h" + #include "lwip/inet.h" + #include "lwip/dns.h" + #include "lwip/err.h" +} + +#include "esp_task_wdt.h" + +#if 0 + #define ASYNC_TCP_SSL_DEBUG(...) do { ets_printf("T %s- ", pcTaskGetTaskName(xTaskGetCurrentTaskHandle())); ets_printf(__VA_ARGS__); } while(0) +#else + #define ASYNC_TCP_SSL_DEBUG(...) +#endif + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + TCP/IP Event Task + + This task processes events that correspond to the various callbacks made by LwIP. The callbacks + are handled by _tcp_* functions, which package the info into events, which are processed by this + task. The purpose of this scheme is ??? (to be able to block or spend arbitrary time in the event + handlers without thereby blocking LwIP???). + * */ + +typedef enum +{ + LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_FIN, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR, LWIP_TCP_ACCEPT, LWIP_TCP_CONNECTED, LWIP_TCP_DNS +} lwip_event_t; + +typedef struct +{ + lwip_event_t event; + void *arg; + + 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 + { + AsyncSSLClient * client; + } accept; + + struct + { + const char * name; + ip_addr_t addr; + } dns; + }; +} lwip_event_packet_t; + +///////////////////////////////////////////////// + +static xQueueHandle _async_queue; +static TaskHandle_t _async_service_task_handle = NULL; + + +static SemaphoreHandle_t _slots_lock; +const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; +static int _closed_slots[_number_of_closed_slots]; + +///////////////////////////////////////////// + +static int _closed_index = []() +{ + _slots_lock = xSemaphoreCreateBinary(); + xSemaphoreGive(_slots_lock); + + for (int i = 0; i < _number_of_closed_slots; ++ i) + { + _closed_slots[i] = 1; + + } + + return 1; +}(); + +///////////////////////////////////////////// + +static inline bool _init_async_event_queue() +{ + if (!_async_queue) + { + _async_queue = xQueueCreate(32, 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, e, portMAX_DELAY) == pdPASS; +} + +///////////////////////////////////////////// + +static inline bool _prepend_async_event(lwip_event_packet_t ** e) +{ + return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS; +} + +///////////////////////////////////////////// + +static inline bool _get_async_event(lwip_event_packet_t ** e) +{ + return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS; +} + +///////////////////////////////////////////// + +static bool _remove_events_with_arg(void * arg) +{ + lwip_event_packet_t * first_packet = NULL; + lwip_event_packet_t * packet = NULL; + + if (!_async_queue) + { + return false; + } + + //figure out which is the first packet so we can keep the order + while (!first_packet) + { + if (xQueueReceive(_async_queue, &first_packet, 0) != pdPASS) + { + return false; + } + + //discard packet if matching + if ((int)first_packet->arg == (int)arg) + { + free(first_packet); + first_packet = NULL; + //return first packet to the back of the queue + } + else if (xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS) + { + return false; + } + } + + while (xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet) + { + if (xQueueReceive(_async_queue, &packet, 0) != pdPASS) + { + return false; + } + + if ((int)packet->arg == (int)arg) + { + free(packet); + packet = NULL; + } + else if (xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS) + { + return false; + } + } + + return true; +} + +///////////////////////////////////////////// + +static void _handle_async_event(lwip_event_packet_t * e) +{ + ATCP_LOGDEBUG1("_handle_async_event: Task Name = ", pcTaskGetTaskName(xTaskGetCurrentTaskHandle())); + + if (e->event == LWIP_TCP_CLEAR) + { + _remove_events_with_arg(e->arg); + } + else if (e->event == LWIP_TCP_RECV) + { + ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_RECV =", (uint32_t) e->recv.pcb); + AsyncSSLClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err); + } + else if (e->event == LWIP_TCP_FIN) + { + ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_FIN =", (uint32_t) e->fin.pcb); + AsyncSSLClient::_s_fin(e->arg, e->fin.pcb, e->fin.err); + } + else if (e->event == LWIP_TCP_SENT) + { + ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_SENT =", (uint32_t) e->sent.pcb); + AsyncSSLClient::_s_sent(e->arg, e->sent.pcb, e->sent.len); + } + else if (e->event == LWIP_TCP_POLL) + { + ATCP_HEXLOGDEBUG1("_handle_async_event: LWIP_TCP_POLL =", (uint32_t) e->poll.pcb); + AsyncSSLClient::_s_poll(e->arg, e->poll.pcb); + } + else if (e->event == LWIP_TCP_ERROR) + { + ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_ERROR =", (uint32_t) e->arg); + ATCP_LOGINFO1("_handle_async_event: LWIP_TCP_ERROR = ", e->error.err); + AsyncSSLClient::_s_error(e->arg, e->error.err); + } + else if (e->event == LWIP_TCP_CONNECTED) + { + ATCP_HEXLOGINFO2("_handle_async_event: LWIP_TCP_CONNECTED =", (uint32_t) e->arg, (uint32_t) e->connected.pcb); + ATCP_LOGINFO1("_handle_async_event: LWIP_TCP_CONNECTED = ", e->connected.err); + AsyncSSLClient::_s_connected(e->arg, e->connected.pcb, e->connected.err); + } + else if (e->event == LWIP_TCP_ACCEPT) + { + ATCP_HEXLOGINFO2("_handle_async_event: LWIP_TCP_ACCEPT =", (uint32_t) e->arg, (uint32_t) e->accept.client); + AsyncSSLServer::_s_accepted(e->arg, e->accept.client); + } + else if (e->event == LWIP_TCP_DNS) + { + ATCP_HEXLOGINFO1("_handle_async_event: LWIP_TCP_DNS =", (uint32_t) e->arg); + ATCP_LOGINFO3("_handle_async_event: LWIP_TCP_DNS, name =", e->dns.name, ", IP =", ipaddr_ntoa(&e->dns.addr)); + AsyncSSLClient::_s_dns_found(e->dns.name, &e->dns.addr, e->arg); + } + + free((void*)(e)); +} + +///////////////////////////////////////////// + +static void _async_service_task(void *pvParameters) +{ + lwip_event_packet_t * packet = NULL; + + for (;;) + { + if (_get_async_event(&packet)) + { +#if CONFIG_ASYNC_TCP_USE_WDT + if (esp_task_wdt_add(NULL) != ESP_OK) + { + ATCP_LOGERROR("Failed to add async task to WDT"); + } +#endif + + if (packet) + _handle_async_event(packet); + else + { + ATCP_LOGERROR("_async_service_task, NUL packet"); + } + +#if CONFIG_ASYNC_TCP_USE_WDT + if (esp_task_wdt_delete(NULL) != ESP_OK) + { + ATCP_LOGERROR("Failed to remove loop task from WDT"); + } +#endif + } + } + + vTaskDelete(NULL); + _async_service_task_handle = NULL; +} + +///////////////////////////////////////////// + +/* + static void _stop_async_task(){ + if(_async_service_task_handle){ + vTaskDelete(_async_service_task_handle); + _async_service_task_handle = NULL; + } + } +*/ + +///////////////////////////////////////////// + +static bool _start_async_task() +{ + if (!_init_async_event_queue()) + { + return false; + } + + if (!_async_service_task_handle) + { + xTaskCreateUniversal(_async_service_task, "async_tcp_ssl", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE); + + if (!_async_service_task_handle) + { + return false; + } + } + + return true; +} + +///////////////////////////////////////////// + +/* + LwIP Callbacks + + The following "_tcp_*" functions are called by LwIP on its thread. They all do nothing but + package the callback info into an event, which is queued for the async event task (see above). + * */ + +static int8_t _tcp_clear_events(void * arg) +{ + lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_CLEAR; + e->arg = arg; + + if (!_prepend_async_event(&e)) + { + free((void*)(e)); + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) +{ + ATCP_HEXLOGDEBUG1("_tcp_connected: pcb =", (uint32_t) pcb); + + lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_CONNECTED; + e->arg = arg; + e->connected.pcb = pcb; + e->connected.err = err; + + if (!_prepend_async_event(&e)) + { + free((void*)(e)); + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) +{ + ATCP_HEXLOGDEBUG1("_tcp_poll: pcb =", (uint32_t) pcb); + + lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_POLL; + e->arg = arg; + e->poll.pcb = pcb; + + if (!_send_async_event(&e)) + { + free((void*)(e)); + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) +{ + lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); + e->arg = arg; + + if (pb) + { + ATCP_HEXLOGDEBUG1("_tcp_recv: pcb =", (uint32_t) pcb); + + e->event = LWIP_TCP_RECV; + e->recv.pcb = pcb; + e->recv.pb = pb; + e->recv.err = err; + } + else + { + ATCP_HEXLOGDEBUG1("_tcp_recv: failed, pcb =", (uint32_t) pcb); + + e->event = LWIP_TCP_FIN; + e->fin.pcb = pcb; + e->fin.err = err; + //close the PCB in LwIP thread + AsyncSSLClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); + } + + if (!_send_async_event(&e)) + { + free((void*)(e)); + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) +{ + ATCP_HEXLOGDEBUG1("_tcp_sent: pcb =", (uint32_t) pcb); + + lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_SENT; + e->arg = arg; + e->sent.pcb = pcb; + e->sent.len = len; + + if (!_send_async_event(&e)) + { + free((void*)(e)); + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +static void _tcp_error(void * arg, int8_t err) +{ + ATCP_HEXLOGDEBUG1("_tcp_error: arg =", (uint32_t) arg); + + lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_ERROR; + e->arg = arg; + 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) +{ + lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); + + //ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg); + ATCP_LOGDEBUG3("_tcp_dns_found: name =", name, ", IP =", ipaddr_ntoa(ipaddr)); + ATCP_HEXLOGDEBUG1("_tcp_dns_found: arg =", (uint32_t) arg); + + e->event = LWIP_TCP_DNS; + e->arg = arg; + 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)); + } +} + +///////////////////////////////////////////// + +//Used to switch out from LwIP thread +static int8_t _tcp_accept(void * arg, AsyncSSLClient * client) +{ + lwip_event_packet_t * e = (lwip_event_packet_t *) malloc(sizeof(lwip_event_packet_t)); + e->event = LWIP_TCP_ACCEPT; + e->arg = arg; + e->accept.client = client; + + if (!_prepend_async_event(&e)) + { + free((void*)(e)); + } + + return ERR_OK; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + TCP/IP API Calls + + The following functions provide stubs to call into LwIP's TCP api functions on the LwIP thread + itself. This ensures there are no race conditions between the application and LwIP. + The way it works is that the `_tcp_xxx` functions synchronously call the corresponding + `_tcp_xxx_api` functions on the LwIP thread using a `tcp_api_call` mechanism provided by LwIP. + The `_tcp_xxx_api` function then finally calls the actual `tcp_xxx` function in LwIP and returns + the result. + * */ + +#include "lwip/priv/tcpip_priv.h" + +typedef struct +{ + struct tcpip_api_call_data call; + tcp_pcb * pcb; + int8_t closed_slot; + 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; + + struct + { + ip_addr_t * addr; + uint16_t port; + } bind; + + uint8_t backlog; + }; +} tcp_api_call_t; + +///////////////////////////////////////////// + +static err_t _tcp_output_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 (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) + { + msg->err = tcp_output(msg->pcb); + } + + return msg->err; +} + +///////////////////////////////////////////// + +static esp_err_t _tcp_output(tcp_pcb * pcb, int8_t closed_slot) +{ + if (!pcb) + { + return ERR_CONN; + } + + tcp_api_call_t msg; + msg.pcb = pcb; + msg.closed_slot = closed_slot; + 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) +{ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = ERR_CONN; + + if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) + { + 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, int8_t closed_slot, const char* data, size_t size, uint8_t apiflags) +{ + if (!pcb) + { + return ERR_CONN; + } + + tcp_api_call_t msg; + msg.pcb = pcb; + msg.closed_slot = closed_slot; + 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 (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) + { + msg->err = 0; + tcp_recved(msg->pcb, msg->received); + } + + return msg->err; +} + +///////////////////////////////////////////// + +static esp_err_t _tcp_recved(tcp_pcb * pcb, int8_t closed_slot, size_t len) +{ + if (!pcb) + { + return ERR_CONN; + } + + tcp_api_call_t msg; + msg.pcb = pcb; + msg.closed_slot = closed_slot; + 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) +{ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = ERR_CONN; + + if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) + { + msg->err = tcp_close(msg->pcb); + } + + return msg->err; +} + +///////////////////////////////////////////// + +static esp_err_t _tcp_close(tcp_pcb * pcb, int8_t closed_slot) +{ + if (!pcb) + { + return ERR_CONN; + } + + tcp_api_call_t msg; + msg.pcb = pcb; + msg.closed_slot = closed_slot; + 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) +{ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = ERR_CONN; + + if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) + { + tcp_abort(msg->pcb); + } + + return msg->err; +} + +///////////////////////////////////////////// + +static esp_err_t _tcp_abort(tcp_pcb * pcb, int8_t closed_slot) +{ + if (!pcb) + { + return ERR_CONN; + } + + tcp_api_call_t msg; + msg.pcb = pcb; + msg.closed_slot = closed_slot; + 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) +{ + tcp_api_call_t * 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, int8_t closed_slot, 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.closed_slot = closed_slot; + 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; +} + +///////////////////////////////////////////// + +static err_t _tcp_bind_api(struct tcpip_api_call_data *api_call_msg) +{ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); + + return msg->err; +} + +///////////////////////////////////////////// + +static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) +{ + if (!pcb) + { + return ESP_FAIL; + } + + tcp_api_call_t msg; + msg.pcb = pcb; + msg.closed_slot = -1; + msg.bind.addr = addr; + msg.bind.port = port; + tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg); + + return msg.err; +} + +///////////////////////////////////////////// + +static err_t _tcp_listen_api(struct tcpip_api_call_data *api_call_msg) +{ + tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; + msg->err = 0; + msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); + + return msg->err; +} + +///////////////////////////////////////////// + +static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) +{ + if (!pcb) + { + return NULL; + } + + tcp_api_call_t msg; + msg.pcb = pcb; + msg.closed_slot = -1; + msg.backlog = backlog ? backlog : 0xFF; + tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg); + + return msg.pcb; +} + +///////////////////////////////////////////// + +// KH +#if ASYNC_TCP_SSL_ENABLED +//#if 1 //ASYNC_TCP_SSL_ENABLED +extern "C" +{ + // The following API stubs are for use in tcp_mbedtls.c + // They are callable from C and take a void* instead of an AsyncSSLClient*. + + esp_err_t _tcp_output4ssl(tcp_pcb * pcb, void* client) + { + // KH + //return _tcp_output(pcb, reinterpret_cast (client) ); + return _tcp_output(pcb, (reinterpret_cast (client) )->getClosed_Slot() ); + ////// + } + + esp_err_t _tcp_write4ssl(tcp_pcb * pcb, const char* data, size_t size, uint8_t apiflags, void* client) + { + // KH + //return _tcp_write(pcb, data, size, apiflags, reinterpret_cast (client) ); + // static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char* data, size_t size, uint8_t apiflags) + // _tcp_write(_pcb, _closed_slot, data, will_send, apiflags); + //return _tcp_write(pcb, _closed_slots, data, size, apiflags, (int8_t) (reinterpret_cast (client) ) ); + return _tcp_write(pcb, (reinterpret_cast (client) )->getClosed_Slot(), data, size, apiflags); + ////// + } + +} +#endif + +////////////////////////////////////////////////////////////////////////////////////// + +/* + Async TCP Client +*/ + +AsyncSSLClient::AsyncSSLClient(tcp_pcb* pcb) + : _connect_cb(0) + , _connect_cb_arg(0) + , _discard_cb(0) + , _discard_cb_arg(0) + , _sent_cb(0) + , _sent_cb_arg(0) + , _error_cb(0) + , _error_cb_arg(0) + , _recv_cb(0) + , _recv_cb_arg(0) + , _pb_cb(0) + , _pb_cb_arg(0) + , _timeout_cb(0) + , _timeout_cb_arg(0) + , _pcb_busy(false) + , _pcb_sent_at(0) + , _ack_pcb(true) + , _rx_last_packet(0) + , _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) + , _cli_cert_len(0) + , _cli_cert(NULL) + , _cli_key_len(0) + , _cli_key(NULL) + , _pcb_secure(false) + , _handshake_done(true) + , _psk_ident(0) + , _psk(0) +#endif // ASYNC_TCP_SSL_ENABLED + , prev(NULL) + , next(NULL) +{ + _pcb = pcb; + _closed_slot = -1; + if (_pcb) { + _allocate_closed_slot(); + _rx_last_packet = millis(); + tcp_arg(_pcb, this); + tcp_recv(_pcb, &_tcp_recv); + tcp_sent(_pcb, &_tcp_sent); + tcp_err(_pcb, &_tcp_error); + tcp_poll(_pcb, &_tcp_poll, 1); + } +} + +///////////////////////////////////////////// + +AsyncSSLClient::~AsyncSSLClient() +{ + if (_pcb) + { + _close(); + } + + _free_closed_slot(); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + Operators + * */ + +AsyncSSLClient& AsyncSSLClient::operator=(const AsyncSSLClient& other) +{ + if (_pcb) + { + _close(); + } + + _pcb = other._pcb; + _closed_slot = other._closed_slot; + + if (_pcb) + { + _rx_last_packet = millis(); + tcp_arg(_pcb, this); + tcp_recv(_pcb, &_tcp_recv); + 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; +} + +///////////////////////////////////////////// + +bool AsyncSSLClient::operator==(const AsyncSSLClient &other) +{ + return _pcb == other._pcb; +} + +///////////////////////////////////////////// + +AsyncSSLClient & AsyncSSLClient::operator+=(const AsyncSSLClient &other) +{ + if (next == NULL) + { + next = (AsyncSSLClient*)(&other); + next->prev = this; + } + else + { + AsyncSSLClient *c = next; + + while (c->next != NULL) + { + c = c->next; + } + + c->next = (AsyncSSLClient*)(&other); + c->next->prev = c; + } + + return *this; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + Callback Setters + * */ + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::onConnect(AcConnectHandlerSSL cb, void* arg) +#else +void AsyncSSLClient::onConnect(AcConnectHandler cb, void* arg) +#endif +{ + _connect_cb = cb; + _connect_cb_arg = arg; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::onDisconnect(AcConnectHandlerSSL cb, void* arg) +#else +void AsyncSSLClient::onDisconnect(AcConnectHandler cb, void* arg) +#endif +{ + _discard_cb = cb; + _discard_cb_arg = arg; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::onAck(AcAckHandlerSSL cb, void* arg) +#else +void AsyncSSLClient::onAck(AcAckHandler cb, void* arg) +#endif +{ + _sent_cb = cb; + _sent_cb_arg = arg; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::onError(AcErrorHandlerSSL cb, void* arg) +#else +void AsyncSSLClient::onError(AcErrorHandler cb, void* arg) +#endif +{ + _error_cb = cb; + _error_cb_arg = arg; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::onData(AcDataHandlerSSL cb, void* arg) +#else +void AsyncSSLClient::onData(AcDataHandler cb, void* arg) +#endif +{ + _recv_cb = cb; + _recv_cb_arg = arg; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::onPacket(AcPacketHandlerSSL cb, void* arg) +#else +void AsyncSSLClient::onPacket(AcPacketHandler cb, void* arg) +#endif +{ + _pb_cb = cb; + _pb_cb_arg = arg; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::onTimeout(AcTimeoutHandlerSSL cb, void* arg) +#else +void AsyncSSLClient::onTimeout(AcTimeoutHandler cb, void* arg) +#endif +{ + _timeout_cb = cb; + _timeout_cb_arg = arg; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::onPoll(AcConnectHandlerSSL cb, void* arg) +#else +void AsyncSSLClient::onPoll(AcConnectHandler cb, void* arg) +#endif +{ + _poll_cb = cb; + _poll_cb_arg = arg; +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + Main Public Methods + * */ + +#if ASYNC_TCP_SSL_ENABLED +bool AsyncSSLClient::connect(IPAddress ip, uint16_t port, bool secure) +{ +#else +bool AsyncSSLClient::connect(IPAddress ip, uint16_t port) +{ +#endif // ASYNC_TCP_SSL_ENABLED + if (_pcb) + { + ATCP_LOGWARN1("connect: already connected, state =", _pcb->state); + return false; + } + + if (!_start_async_task()) + { + ATCP_LOGERROR("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) + { + ATCP_LOGERROR("connect: NULL pcb"); + + 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); + tcp_sent(pcb, &_tcp_sent); + tcp_poll(pcb, &_tcp_poll, 1); + //_tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected); + _tcp_connect(pcb, _closed_slot, &addr, port, (tcp_connected_fn)&_tcp_connected); + + return true; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +bool AsyncSSLClient::connect(const char* host, uint16_t port, bool secure) +#else +bool AsyncSSLClient::connect(const char* host, uint16_t port) +#endif // ASYNC_TCP_SSL_ENABLED +{ + ip_addr_t addr; + + if (!_start_async_task()) + { + ATCP_LOGERROR("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) + { +#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; + } + + ATCP_LOGERROR1("connect: error =", err); + + return false; +} + +///////////////////////////////////////////// + +void AsyncSSLClient::close(bool now) +{ + if (_pcb) + { + _tcp_recved(_pcb, _closed_slot, _rx_ack_len); + } + + _close(); +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::abort() +{ + if (_pcb) + { + _tcp_abort(_pcb, _closed_slot ); + _pcb = NULL; + } + + return ERR_ABRT; +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED + +///////////////////////////////////////////// + +void AsyncSSLClient::setRootCa(const char* rootca, const size_t len) +{ + _root_ca = (char*)rootca; + _root_ca_len = len; +} + +///////////////////////////////////////////// + +void AsyncSSLClient::setClientCert(const char* cli_cert, const size_t len) +{ + _cli_cert = (char*)cli_cert; + _cli_cert_len = len; +} + +///////////////////////////////////////////// + +void AsyncSSLClient::setClientKey(const char* cli_key, const size_t len) +{ + _cli_key = (char*)cli_key; + _cli_key_len = len; +} + +///////////////////////////////////////////// + +void AsyncSSLClient::setPsk(const char* psk_ident, const char* psk) +{ + _psk_ident = psk_ident; + _psk = psk; +} + +///////////////////////////////////////////// + +#endif // ASYNC_TCP_SSL_ENABLED + +///////////////////////////////////////////// + +size_t AsyncSSLClient::space() +{ + if ((_pcb != NULL) && (_pcb->state == 4)) + { + return tcp_sndbuf(_pcb); + } + + return 0; +} + +///////////////////////////////////////////// + +size_t AsyncSSLClient::add(const char* data, size_t size, uint8_t apiflags) +{ + if (!_pcb || size == 0 || data == NULL) + { + return 0; + } + + size_t room = space(); + + if (!room) + { + return 0; + } + +#if ASYNC_TCP_SSL_ENABLED + if (_pcb_secure) + { + int sent = tcp_ssl_write(_pcb, (uint8_t*)data, size); + + ASYNC_TCP_SSL_DEBUG("add() tcp_ssl_write size = %d\n", sent); + + if (sent >= 0) + { + // @ToDo: ??? + //_tx_unacked_len += sent; + return sent; + } + + ATCP_LOGINFO1("add() done, tcp_ssl_write size =", 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, _closed_slot, data, will_send, apiflags); + + if (err != ERR_OK) + { + return 0; + } + + return will_send; +} + +///////////////////////////////////////////// + +bool AsyncSSLClient::send() +{ +#if ASYNC_TCP_SSL_ENABLED + + //delay(1); // 5 is OK + vTaskDelay(1 / portTICK_PERIOD_MS); + + //if(_pcb_secure) return true; +#endif // ASYNC_TCP_SSL_ENABLED + int8_t err = ERR_OK; + err = _tcp_output(_pcb, _closed_slot); + + if (err == ERR_OK) + { + _pcb_busy = true; + _pcb_sent_at = millis(); + return true; + } + + return false; +} + +///////////////////////////////////////////// + +size_t AsyncSSLClient::ack(size_t len) +{ + if (len > _rx_ack_len) + len = _rx_ack_len; + + if (len) + { + _tcp_recved(_pcb, _closed_slot, len); + } + + _rx_ack_len -= len; + + return len; +} + +///////////////////////////////////////////// + +void AsyncSSLClient::ackPacket(struct pbuf * pb) +{ + if (!pb) + { + return; + } + + _tcp_recved(_pcb, _closed_slot, pb->len); + pbuf_free(pb); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + Main Private Methods + * */ + +int8_t AsyncSSLClient::_close() +{ + //ets_printf("X: 0x%08x\n", (uint32_t)this); + int8_t err = ERR_OK; + + 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); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + _tcp_clear_events(this); + err = _tcp_close(_pcb, _closed_slot); + + if (err != ERR_OK) + { + err = abort(); + } + + _pcb = NULL; + + if (_discard_cb) + { + _discard_cb(_discard_cb_arg, this); + } + } + + return err; +} + +///////////////////////////////////////////// + +void AsyncSSLClient::_allocate_closed_slot() +{ + xSemaphoreTake(_slots_lock, portMAX_DELAY); + uint32_t closed_slot_min_index = 0; + + for (int i = 0; i < _number_of_closed_slots; ++ i) + { + if ((_closed_slot == -1 || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) + { + closed_slot_min_index = _closed_slots[i]; + _closed_slot = i; + } + } + + if (_closed_slot != -1) + { + _closed_slots[_closed_slot] = 0; + } + + xSemaphoreGive(_slots_lock); +} + +///////////////////////////////////////////// + +void AsyncSSLClient::_free_closed_slot() +{ + if (_closed_slot != -1) + { + _closed_slots[_closed_slot] = _closed_index; + _closed_slot = -1; + ++ _closed_index; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + Private Callbacks + * */ + +int8_t AsyncSSLClient::_connected(void* pcb, int8_t err) +{ + _pcb = reinterpret_cast(pcb); + + if (_pcb) + { + _rx_last_packet = millis(); + _pcb_busy = false; + // 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 (_psk_ident != NULL and _psk != NULL) + { + err = tcp_ssl_new_psk_client(_pcb, this, _psk_ident, _psk) < 0; + } + else + { + err = tcp_ssl_new_client(_pcb, this, _hostname.empty() ? NULL : _hostname.c_str(), + _root_ca, _root_ca_len, _cli_cert, _cli_cert_len, _cli_key, _cli_key_len) < 0; + } + + if (err) + { + ATCP_LOGERROR("_connected: error => closing"); + + return _close(); + } + + 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 AsyncSSLClient::_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); + + if (_pcb->state == LISTEN) + { + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + } + _pcb = NULL; + } + + if (_error_cb) + { + _error_cb(_error_cb_arg, this, err); + } + + if (_discard_cb) + { + _discard_cb(_discard_cb_arg, this); + } +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLClient::_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 AsyncSSLClient::_lwip_fin(tcp_pcb* pcb, int8_t err) +{ + if (!_pcb || pcb != _pcb) + { + ATCP_HEXLOGDEBUG2("_lwip_fin: pcb/_pcb =", (uint32_t)pcb, (uint32_t)_pcb); + + return ERR_OK; + } + + tcp_arg(_pcb, NULL); + + if (_pcb->state == LISTEN) + { + tcp_sent(_pcb, NULL); + tcp_recv(_pcb, NULL); + tcp_err(_pcb, NULL); + tcp_poll(_pcb, NULL, 0); + } + + if (tcp_close(_pcb) != ERR_OK) + { + tcp_abort(_pcb); + } + + _free_closed_slot(); + _pcb = NULL; + + return ERR_OK; +} + +///////////////////////////////////////////// + +//In Async Thread +int8_t AsyncSSLClient::_fin(tcp_pcb* pcb, int8_t err) +{ + _tcp_clear_events(this); + + if (_discard_cb) + { + _discard_cb(_discard_cb_arg, this); + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::_sent(tcp_pcb* pcb, uint16_t len) +{ + _rx_last_packet = millis(); + + ATCP_LOGINFO1("_sent: len =", len); + + _pcb_busy = false; + + if (_sent_cb) + { + _sent_cb(_sent_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 != NULL) + { + _rx_last_packet = millis(); + pbuf *nxt = pb->next; + pb->next = NULL; + +#if ASYNC_TCP_SSL_ENABLED + if (_pcb_secure) + { + ATCP_LOGINFO1("_recv: tot_len =", pb->tot_len); + + int err = tcp_ssl_read(pcb, pb); + // tcp_ssl_read always processes the full pbuf, so ack all of it + // KH + //_tcp_recved(pcb, pb->len); + _tcp_recved(pcb, _closed_slot, pb->len); + ////// + pbuf_free(pb); + // handle errors + if (err < 0) + { + if (err != MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) + { + ATCP_LOGERROR1("_recv: err =", err); + + _close(); + } + + return ERR_BUF; // for lack of a better error value + } + + } else +#endif // ASYNC_TCP_SSL_ENABLED + { + //we should not ack before we assimilate the data + _ack_pcb = true; + + if (_pb_cb) + { + _pb_cb(_pb_cb_arg, this, pb); + } + else + { + if (_recv_cb) + { + _recv_cb(_recv_cb_arg, this, pb->payload, pb->len); + } + + if (!_ack_pcb) + { + _rx_ack_len += pb->len; + } + else if (_pcb) + { + _tcp_recved(_pcb, _closed_slot, pb->len); + } + pbuf_free(pb); + } + } + + pb = nxt; + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::_poll(tcp_pcb* pcb) +{ + if (!_pcb) + { + ATCP_LOGWARN("_poll: NULL pcb"); + + return ERR_OK; + } + + if (pcb != _pcb) + { + ATCP_HEXLOGERROR2("_poll: diff pcb/_pcb =", (uint32_t) pcb, (uint32_t) _pcb); + + return ERR_OK; + } + + uint32_t now = millis(); + + // ACK Timeout + if (_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout) + { + _pcb_busy = false; + + ATCP_LOGWARN1("_poll: ack timeout, state =", pcb->state); + + if (_timeout_cb) + _timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at)); + + return ERR_OK; + } + + // RX Timeout + if (_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)) + { + ATCP_LOGWARN1("_poll: rx timeout, state =", pcb->state); + + _close(); + return ERR_OK; + } + +#if ASYNC_TCP_SSL_ENABLED + if (_pcb_secure && !_handshake_done && (now - _rx_last_packet) >= SSL_HANDSHAKE_TIMEOUT) + { + ATCP_LOGWARN1("_poll: ssl handshake timeout, state =", pcb->state); + + _close(); + return ERR_OK; + } +#endif // ASYNC_TCP_SSL_ENABLED + + // Everything is fine + if (_poll_cb) + { + _poll_cb(_poll_cb_arg, this); + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +void AsyncSSLClient::_dns_found(struct ip_addr *ipaddr) +{ + if (ipaddr && ipaddr->u_addr.ip4.addr) + { +#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); + } + + if (_discard_cb) + { + _discard_cb(_discard_cb_arg, this); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + Public Helper Methods + * */ + +void AsyncSSLClient::stop() +{ + close(false); +} + +///////////////////////////////////////////// + +bool AsyncSSLClient::free() +{ + if (!_pcb) + { + return true; + } + + if (_pcb->state == 0 || _pcb->state > 4) + { + return true; + } + + return false; +} + +///////////////////////////////////////////// + +size_t AsyncSSLClient::write(const char* data) +{ + if (data == NULL) + { + 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 IPAddress(getRemoteAddress()); +} + +///////////////////////////////////////////// + +uint16_t AsyncSSLClient::remotePort() +{ + return getRemotePort(); +} + +///////////////////////////////////////////// + +IPAddress AsyncSSLClient::localIP() +{ + return IPAddress(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; + } + + return _pcb->state == 4; +} + +///////////////////////////////////////////// + +bool AsyncSSLClient::connecting() +{ + if (!_pcb) + { + return false; + } + + return _pcb->state > 0 && _pcb->state < 4; +} + +///////////////////////////////////////////// + +bool AsyncSSLClient::disconnecting() +{ + if (!_pcb) + { + return false; + } + + return _pcb->state > 4 && _pcb->state < 10; +} + +///////////////////////////////////////////// + +bool AsyncSSLClient::disconnected() +{ + if (!_pcb) + { + return true; + } + + return _pcb->state == 0 || _pcb->state == 10; +} + +///////////////////////////////////////////// + +bool AsyncSSLClient::freeable() +{ + if (!_pcb) + { + return true; + } + + return _pcb->state == 0 || _pcb->state > 4; +} + +///////////////////////////////////////////// + +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_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 -55: return "DNS failed"; + default: return "UNKNOWN"; + } +} + +///////////////////////////////////////////// + +const char * AsyncSSLClient::stateToString() +{ + switch (state()) + { + case 0: return "Closed"; + case 1: return "Listen"; + case 2: return "SYN Sent"; + case 3: return "SYN Received"; + case 4: return "Established"; + case 5: return "FIN Wait 1"; + case 6: return "FIN Wait 2"; + case 7: return "Close Wait"; + case 8: return "Closing"; + case 9: return "Last ACK"; + case 10: return "Time Wait"; + default: return "UNKNOWN"; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + Static Callbacks (LwIP C2C++ interconnect) + * */ + +void AsyncSSLClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) +{ + reinterpret_cast(arg)->_dns_found(ipaddr); +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::_s_poll(void * arg, struct tcp_pcb * pcb) +{ + return reinterpret_cast(arg)->_poll(pcb); +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) +{ + return reinterpret_cast(arg)->_recv(pcb, pb, err); +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::_s_fin(void * arg, struct tcp_pcb * pcb, int8_t err) +{ + return reinterpret_cast(arg)->_fin(pcb, err); +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::_s_lwip_fin(void * arg, struct tcp_pcb * pcb, int8_t err) +{ + return reinterpret_cast(arg)->_lwip_fin(pcb, err); +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) +{ + return reinterpret_cast(arg)->_sent(pcb, len); +} + +///////////////////////////////////////////// + +void AsyncSSLClient::_s_error(void * arg, int8_t err) +{ + reinterpret_cast(arg)->_error(err); +} + +///////////////////////////////////////////// + +int8_t AsyncSSLClient::_s_connected(void * arg, void * pcb, int8_t err) +{ + return reinterpret_cast(arg)->_connected(pcb, err); +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED + +///////////////////////////////////////////// + +void AsyncSSLClient::_s_data(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len) +{ + AsyncSSLClient *c = reinterpret_cast(arg); + + if (c->_recv_cb) + c->_recv_cb(c->_recv_cb_arg, c, data, len); +} + +///////////////////////////////////////////// + +void AsyncSSLClient::_s_handshake(void *arg, struct tcp_pcb *tcp, struct tcp_ssl_pcb* ssl) +{ + AsyncSSLClient *c = reinterpret_cast(arg); + c->_handshake_done = true; + + if (c->_connect_cb) + c->_connect_cb(c->_connect_cb_arg, c); +} + +///////////////////////////////////////////// + +void AsyncSSLClient::_s_ssl_error(void *arg, struct tcp_pcb *tcp, int8_t err) +{ + reinterpret_cast(arg)->_ssl_error(err); +} + +///////////////////////////////////////////// + +#endif // ASYNC_TCP_SSL_ENABLED + +////////////////////////////////////////////////////////////////////////////////////////// + +/* + Async TCP Server +*/ + +AsyncSSLServer::AsyncSSLServer(IPAddress addr, uint16_t port) + : _port(port) + , _addr(addr) + , _noDelay(false) + , _pcb(0) + , _connect_cb(0) + , _connect_cb_arg(0) +{} + +///////////////////////////////////////////// + +AsyncSSLServer::AsyncSSLServer(uint16_t port) + : _port(port) + , _addr((uint32_t) IPADDR_ANY) + , _noDelay(false) + , _pcb(0) + , _connect_cb(0) + , _connect_cb_arg(0) +{} + +///////////////////////////////////////////// + +AsyncSSLServer::~AsyncSSLServer() +{ + end(); +} + +///////////////////////////////////////////// + +#if ASYNC_TCP_SSL_ENABLED +void AsyncSSLServer::onClient(AcConnectHandlerSSL cb, void* arg) +#else +void AsyncSSLServer::onClient(AcConnectHandler cb, void* arg) +#endif +{ + _connect_cb = cb; + _connect_cb_arg = arg; +} + +///////////////////////////////////////////// + +void AsyncSSLServer::begin() +{ + if (_pcb) + { + return; + } + + if (!_start_async_task()) + { + ATCP_LOGERROR("begin: failed to start task"); + + return; + } + + int8_t err; + _pcb = tcp_new_ip_type(IPADDR_TYPE_V4); + + if (!_pcb) + { + ATCP_LOGERROR("begin: NULL pcb"); + + return; + } + + ip_addr_t local_addr; + local_addr.type = IPADDR_TYPE_V4; + local_addr.u_addr.ip4.addr = (uint32_t) _addr; + err = _tcp_bind(_pcb, &local_addr, _port); + + if (err != ERR_OK) + { + _tcp_close(_pcb, -1); + + ATCP_LOGERROR1("begin: bind error, err =", err); + + return; + } + + static uint8_t backlog = 5; + _pcb = _tcp_listen_with_backlog(_pcb, backlog); + + if (!_pcb) + { + ATCP_LOGERROR("begin: NULL listen_pcb"); + + return; + } + + tcp_arg(_pcb, (void*) this); + tcp_accept(_pcb, &_s_accept); +} + +///////////////////////////////////////////// + +void AsyncSSLServer::end() +{ + if (_pcb) + { + tcp_arg(_pcb, NULL); + tcp_accept(_pcb, NULL); + + if (tcp_close(_pcb) != ERR_OK) + { + _tcp_abort(_pcb, -1); + } + + _pcb = NULL; + } +} + +///////////////////////////////////////////// + +//runs on LwIP thread +int8_t AsyncSSLServer::_accept(tcp_pcb* pcb, int8_t err) +{ + ATCP_HEXLOGDEBUG1("_accept: pcb =", (uint32_t) pcb); + + if (_connect_cb) + { + AsyncSSLClient *c = new AsyncSSLClient(pcb); + + if (c) + { + c->setNoDelay(_noDelay); + return _tcp_accept(this, c); + } + } + + if (tcp_close(pcb) != ERR_OK) + { + tcp_abort(pcb); + } + + ATCP_LOGERROR("_accept: fail"); + + return ERR_OK; +} + +///////////////////////////////////////////// + +int8_t AsyncSSLServer::_accepted(AsyncSSLClient* client) +{ + if (_connect_cb) + { + _connect_cb(_connect_cb_arg, client); + } + + return ERR_OK; +} + +///////////////////////////////////////////// + +void AsyncSSLServer::setNoDelay(bool nodelay) +{ + _noDelay = nodelay; +} + +///////////////////////////////////////////// + +bool AsyncSSLServer::getNoDelay() +{ + return _noDelay; +} + +///////////////////////////////////////////// + +uint8_t AsyncSSLServer::status() +{ + if (!_pcb) + { + return 0; + } + + return _pcb->state; +} + +///////////////////////////////////////////// + +int8_t AsyncSSLServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err) +{ + return reinterpret_cast(arg)->_accept(pcb, err); +} + +///////////////////////////////////////////// + +int8_t AsyncSSLServer::_s_accepted(void *arg, AsyncSSLClient* client) +{ + return reinterpret_cast(arg)->_accepted(client); +} + +///////////////////////////////////////////// + +#endif /* ASYNCTCP_SSL_IML_H */ diff --git a/src/tcp_mbedtls.c b/src/tcp_mbedtls.c index 09f474c..f2b15fe 100644 --- a/src/tcp_mbedtls.c +++ b/src/tcp_mbedtls.c @@ -15,12 +15,13 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - Version: 1.1.0 + Version: 1.2.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 21/10/2021 Initial coding to support only ESP32 1.1.0 K Hoang 22/10/2021 Fix bug. Enable coexistence with AsyncTCP + 1.2.0 K Hoang 23/01/2022 Fix `multiple-definitions` linker error *****************************************************************************************************************************/ #ifndef _ASYNC_TCP_SSL_LOGLEVEL_ @@ -120,6 +121,8 @@ typedef struct tcp_ssl_pcb tcp_ssl_t; static tcp_ssl_t * tcp_ssl_array = NULL; static int tcp_ssl_next_fd = 0; +///////////////////////////////////////////// + // tcp_ssl_recv attempts to read up to len bytes into buf from data already received. // It is called by mbedtls. int tcp_ssl_recv(void *ctx, unsigned char *buf, size_t len) @@ -149,6 +152,8 @@ int tcp_ssl_recv(void *ctx, unsigned char *buf, size_t len) return recv_len; } +///////////////////////////////////////////// + // tcp_ssl_send attempts to send len bytes from buf. // It is called by mbedtls. int tcp_ssl_send(void *ctx, const unsigned char *buf, size_t len) @@ -230,11 +235,15 @@ int tcp_ssl_send(void *ctx, const unsigned char *buf, size_t len) return tcp_len; } +///////////////////////////////////////////// + uint8_t tcp_ssl_has_client() { return _tcp_ssl_has_client; } +///////////////////////////////////////////// + tcp_ssl_t * tcp_ssl_new(struct tcp_pcb *tcp, void* arg) { @@ -280,6 +289,8 @@ tcp_ssl_t * tcp_ssl_new(struct tcp_pcb *tcp, void* arg) return new_item; } +///////////////////////////////////////////// + tcp_ssl_t* tcp_ssl_get(struct tcp_pcb *tcp) { if (tcp == NULL) @@ -297,6 +308,8 @@ tcp_ssl_t* tcp_ssl_get(struct tcp_pcb *tcp) return item; } +///////////////////////////////////////////// + int tcp_ssl_new_client(struct tcp_pcb *tcp, void *arg, const char* hostname, const char* root_ca, const size_t root_ca_len, const char* cli_cert, const size_t cli_cert_len, const char* cli_key, const size_t cli_key_len) { @@ -468,6 +481,8 @@ int tcp_ssl_new_client(struct tcp_pcb *tcp, void *arg, const char* hostname, con return ERR_OK; } +///////////////////////////////////////////// + // Open an SSL connection using a PSK (pre-shared-key) cipher suite. int tcp_ssl_new_psk_client(struct tcp_pcb *tcp, void *arg, const char* psk_ident, const char* pskey) { @@ -603,6 +618,8 @@ int tcp_ssl_new_psk_client(struct tcp_pcb *tcp, void *arg, const char* psk_ident return ERR_OK; } +///////////////////////////////////////////// + // tcp_ssl_write writes len bytes from data into the TLS connection. I.e., data is plaintext, gets // encrypted, and then transmitted on the TCP connection. int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len) @@ -645,6 +662,8 @@ int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len) return tcp_ssl->last_wr; } +///////////////////////////////////////////// + /* typedef enum { @@ -673,6 +692,8 @@ int tcp_ssl_write(struct tcp_pcb *tcp, uint8_t *data, size_t len) */ +///////////////////////////////////////////// + // tcp_ssl_read is a callback that reads from the TLS connection, i.e., it calls mbedtls, which then // tries to read from the TCP connection and decrypts it, tcp_ssl_read then calls the application's // onData callback with the decrypted data. @@ -806,6 +827,8 @@ int tcp_ssl_read(struct tcp_pcb *tcp, struct pbuf *p) return total_bytes >= 0 ? 0 : total_bytes; // return error code } +///////////////////////////////////////////// + int tcp_ssl_free(struct tcp_pcb *tcp) { TCP_SSL_DEBUG("tcp_ssl_free(%x)\n", tcp); @@ -873,11 +896,15 @@ int tcp_ssl_free(struct tcp_pcb *tcp) return 0; } +///////////////////////////////////////////// + bool tcp_ssl_has(struct tcp_pcb *tcp) { return tcp_ssl_get(tcp) != NULL; } +///////////////////////////////////////////// + void tcp_ssl_arg(struct tcp_pcb *tcp, void * arg) { tcp_ssl_t * item = tcp_ssl_get(tcp); @@ -888,6 +915,8 @@ void tcp_ssl_arg(struct tcp_pcb *tcp, void * arg) } } +///////////////////////////////////////////// + void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg) { tcp_ssl_t * item = tcp_ssl_get(tcp); @@ -898,6 +927,8 @@ void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg) } } +///////////////////////////////////////////// + void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg) { tcp_ssl_t * item = tcp_ssl_get(tcp); @@ -908,6 +939,8 @@ void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg) } } +///////////////////////////////////////////// + void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg) { tcp_ssl_t * item = tcp_ssl_get(tcp); @@ -918,4 +951,6 @@ void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg) } } +///////////////////////////////////////////// + //#endif // ASYNC_TCP_SSL_ENABLED diff --git a/src/tcp_mbedtls.h b/src/tcp_mbedtls.h index 982312d..f15d9d8 100644 --- a/src/tcp_mbedtls.h +++ b/src/tcp_mbedtls.h @@ -15,12 +15,13 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - Version: 1.1.0 + Version: 1.2.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K Hoang 21/10/2021 Initial coding to support only ESP32 1.1.0 K Hoang 22/10/2021 Fix bug. Enable coexistence with AsyncTCP + 1.2.0 K Hoang 23/01/2022 Fix `multiple-definitions` linker error *****************************************************************************************************************************/ #ifndef LWIPR_MBEDTLS_H @@ -47,14 +48,20 @@ extern "C" { #define ERR_TCP_SSL_INVALID_CLIENTFD_DATA -104 #define ERR_TCP_SSL_INVALID_DATA -105 +///////////////////////////////////////////// + struct tcp_pcb; struct pbuf; struct tcp_ssl_pcb; +///////////////////////////////////////////// + typedef void (* tcp_ssl_data_cb_t)(void *arg, struct tcp_pcb *tcp, uint8_t * data, size_t len); typedef void (* tcp_ssl_handshake_cb_t)(void *arg, struct tcp_pcb *tcp, struct tcp_ssl_pcb* ssl); typedef void (* tcp_ssl_error_cb_t)(void *arg, struct tcp_pcb *tcp, int8_t error); +///////////////////////////////////////////// + uint8_t tcp_ssl_has_client(); int tcp_ssl_new_client(struct tcp_pcb *tcp, void *arg, const char* hostname, const char* root_ca, const size_t root_ca_len, const char* cli_cert, const size_t cli_cert_len, const char* cli_key, const size_t cli_key_len); @@ -69,6 +76,8 @@ void tcp_ssl_data(struct tcp_pcb *tcp, tcp_ssl_data_cb_t arg); void tcp_ssl_handshake(struct tcp_pcb *tcp, tcp_ssl_handshake_cb_t arg); void tcp_ssl_err(struct tcp_pcb *tcp, tcp_ssl_error_cb_t arg); +///////////////////////////////////////////// + #ifdef __cplusplus } #endif