diff --git a/README.md b/README.md index 512f0d0..07feba8 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ Async MQTT client for ESP8266 and ESP32 ============================= -[![Build Status](https://github.com/HeMan/async-mqtt-client/actions/workflows/push.yml/badge.svg)](https://github.com/HeMan/async-mqtt-client/actions/workflows/push.yml) +[![Build Status](https://github.com/esphome-libs/async-mqtt-client/actions/workflows/push.yml/badge.svg)](https://github.com/esphome-libs/async-mqtt-client/actions/workflows/push.yml) A maintained fork of the [AsyncMQTTClient](https://github.com/marvinroger/async-mqtt-client) library by [@marvinroger](https://github.com/marvinroger) for [ESPHome](https://esphome.io). -An Arduino for ESP8266 and ESP32 asynchronous [MQTT](http://mqtt.org/) client implementation, built on [me-no-dev/ESPAsyncTCP (ESP8266)](https://github.com/me-no-dev/ESPAsyncTCP) | [me-no-dev/AsyncTCP (ESP32)](https://github.com/me-no-dev/AsyncTCP) . +An Arduino for ESP8266 and ESP32 asynchronous [MQTT](http://mqtt.org/) client implementation, built on [esphome-libs/ESPAsyncTCP (ESP8266)](https://github.com/esphome-libs/ESPAsyncTCP) | [me-no-dev/AsyncTCP (ESP32)](https://github.com/me-no-dev/AsyncTCP) . ## Features * Compliant with the 3.1.1 version of the protocol diff --git a/docs/1.-Getting-started.md b/docs/1.-Getting-started.md index 2319b59..73e02ad 100644 --- a/docs/1.-Getting-started.md +++ b/docs/1.-Getting-started.md @@ -12,10 +12,10 @@ There are two ways to install AsyncMqttClient. ### 1a. For the Arduino IDE -1. Download the [corresponding release](https://github.com/marvinroger/async-mqtt-client/releases/latest) +1. Download the [corresponding release](https://github.com/esphome-libs/async-mqtt-client/releases/latest) 2. Load the `.zip` with **Sketch → Include Library → Add .ZIP Library** -AsyncMqttClient has 1 dependency: [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP). Download the [.zip](https://github.com/me-no-dev/ESPAsyncTCP/archive/master.zip) and install it with the same method as above. +AsyncMqttClient has 1 dependency: [ESPAsyncTCP](https://github.com/esphome-libs/ESPAsyncTCP). Download the [.zip](https://github.com/esphome-libs/ESPAsyncTCP/archive/refs/heads/main.zip) and install it with the same method as above. ## Fully-featured sketch diff --git a/docs/5.-Troubleshooting.md b/docs/5.-Troubleshooting.md index a59672b..ba0b247 100644 --- a/docs/5.-Troubleshooting.md +++ b/docs/5.-Troubleshooting.md @@ -1,3 +1,5 @@ # Troubleshooting -To be completed when issues arise. +## TLS fingerprint disconnects on ESP8266 + +With `ASYNC_TCP_SSL_BEARSSL`, fingerprint mismatches are rejected during the TLS handshake and surface as `AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT`. diff --git a/library.json b/library.json index 5106591..a8a8dc2 100644 --- a/library.json +++ b/library.json @@ -10,16 +10,16 @@ "repository": { "type": "git", - "url": "https://github.com/HeMan/async-mqtt-client.git" + "url": "https://github.com/esphome-libs/async-mqtt-client.git" }, - "version": "2.1.0", + "version": "2.2.0", "frameworks": "arduino", "platforms": ["espressif8266", "espressif32", "rp2040", "libretiny"], "dependencies": [ { "owner": "esphome", - "name": "ESPAsyncTCP-esphome", - "version": "^2.0.0", + "name": "ESPAsyncTCP", + "version": "2.1.0", "platforms": "espressif8266" }, { diff --git a/src/AsyncMqttClient.cpp b/src/AsyncMqttClient.cpp index 01bb820..71b286d 100644 --- a/src/AsyncMqttClient.cpp +++ b/src/AsyncMqttClient.cpp @@ -1,5 +1,24 @@ #include "AsyncMqttClient.hpp" +#include + +#if defined(ESP8266) && ASYNC_TCP_SSL_ENABLED && ASYNC_TCP_SSL_BEARSSL +namespace { +constexpr int MQTT_TLS_IN_BUF_SIZE = 4421; +constexpr int MQTT_TLS_OUT_BUF_SIZE = 1349; + +SSL_CTX_PARAMS makeSslParams(const std::vector>& fingerprints) { + SSL_CTX_PARAMS sslParams{}; + sslParams.use_insecure = true; + sslParams.use_fingerprint = true; + sslParams.iobuf_in_size = MQTT_TLS_IN_BUF_SIZE; + sslParams.iobuf_out_size = MQTT_TLS_OUT_BUF_SIZE; + memcpy(sslParams.fingerprint, fingerprints.front().data(), SHA1_SIZE); + return sslParams; +} +} +#endif + AsyncMqttClient::AsyncMqttClient() : _connected(false) , _connectPacketNotEnoughSpace(false) @@ -180,7 +199,7 @@ void AsyncMqttClient::_clear() { void AsyncMqttClient::_onConnect(AsyncClient* client) { (void)client; -#if ASYNC_TCP_SSL_ENABLED +#if ASYNC_TCP_SSL_ENABLED && !(defined(ESP8266) && ASYNC_TCP_SSL_BEARSSL) if (_secure && _secureServerFingerprints.size() > 0) { SSL* clientSsl = _client.getSSL(); @@ -324,7 +343,7 @@ void AsyncMqttClient::_onConnect(AsyncClient* client) { sendbuffer[3] = 'Q'; sendbuffer[4] = 'T'; sendbuffer[5] = 'T'; - + sendbuffer[6] = protocolLevel[0]; sendbuffer[7] = connectFlags[0]; sendbuffer[8] = keepAliveBytes[0]; @@ -374,7 +393,13 @@ void AsyncMqttClient::_onDisconnect(AsyncClient* client) { void AsyncMqttClient::_onError(AsyncClient* client, int8_t error) { (void)client; +#if defined(ESP8266) && ASYNC_TCP_SSL_ENABLED && ASYNC_TCP_SSL_BEARSSL + if (_secure && !_secureServerFingerprints.empty() && error == -56) { + _tlsBadFingerprint = true; + } +#else (void)error; +#endif // _onDisconnect called anyway } @@ -618,6 +643,12 @@ void AsyncMqttClient::_onPubComp(uint16_t packetId) { for (auto callback : _onPublishUserCallbacks) callback(packetId); } +void AsyncMqttClient::loop() { + _client.send(); + _onPoll(&_client); + _client.send(); +} + bool AsyncMqttClient::_sendPing() { char fixedHeader[2]; fixedHeader[0] = AsyncMqttClientInternals::PacketType.PINGREQ; @@ -711,6 +742,13 @@ bool AsyncMqttClient::connected() const { void AsyncMqttClient::connect() { if (_connected) return; +#if defined(ESP8266) && ASYNC_TCP_SSL_ENABLED && ASYNC_TCP_SSL_BEARSSL + if (_secure && !_secureServerFingerprints.empty()) { + SSL_CTX_PARAMS sslParams = makeSslParams(_secureServerFingerprints); + _client.setSSLParams(sslParams); + } +#endif + #if ASYNC_TCP_SSL_ENABLED if (_useIp) { _client.connect(_ip, _port, _secure); @@ -877,11 +915,24 @@ uint16_t AsyncMqttClient::publish(const char* topic, uint8_t qos, bool retain, c packetIdBytes[1] = packetId & 0xFF; } - _client.add(fixedHeader, 1 + remainingLengthLength); - _client.add(topicLengthBytes, 2); - _client.add(topic, topicLength); - if (qos != 0) _client.add(packetIdBytes, 2); - if (payload != nullptr) _client.add(payload, payloadLength); + auto packet = std::make_unique(neededSpace); + size_t pos = 0; + memcpy(packet.get() + pos, fixedHeader, 1 + remainingLengthLength); + pos += 1 + remainingLengthLength; + memcpy(packet.get() + pos, topicLengthBytes, 2); + pos += 2; + memcpy(packet.get() + pos, topic, topicLength); + pos += topicLength; + if (qos != 0) { + memcpy(packet.get() + pos, packetIdBytes, 2); + pos += 2; + } + if (payload != nullptr) { + memcpy(packet.get() + pos, payload, payloadLength); + pos += payloadLength; + } + + _client.add(packet.get(), pos); _client.send(); _lastClientActivity = millis(); diff --git a/src/AsyncMqttClient.hpp b/src/AsyncMqttClient.hpp index 912e4ca..4eaeb7f 100644 --- a/src/AsyncMqttClient.hpp +++ b/src/AsyncMqttClient.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -19,8 +20,11 @@ #error Platform not supported #endif -#if ASYNC_TCP_SSL_ENABLED +#if ASYNC_TCP_SSL_ENABLED && !(defined(ESP8266) && ASYNC_TCP_SSL_BEARSSL) #include +#endif + +#if ASYNC_TCP_SSL_ENABLED #define SHA1_SIZE 20 #endif @@ -80,6 +84,7 @@ class AsyncMqttClient { AsyncMqttClient& onPublish(AsyncMqttClientInternals::OnPublishUserCallback callback); bool connected() const; + void loop(); void connect(); void disconnect(bool force = false); uint16_t subscribe(const char* topic, uint8_t qos); @@ -150,7 +155,7 @@ class AsyncMqttClient { // TCP void _onConnect(AsyncClient* client); void _onDisconnect(AsyncClient* client); - static void _onError(AsyncClient* client, int8_t error); + void _onError(AsyncClient* client, int8_t error); void _onTimeout(AsyncClient* client, uint32_t time); static void _onAck(AsyncClient* client, size_t len, uint32_t time); void _onData(AsyncClient* client, uint8_t* data, size_t len);