From 8795fdf0f575704bf141111ae9b6902cd2748291 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 16 Nov 2017 12:41:05 -0800 Subject: [PATCH 01/20] Create new signature to permit cleaner user code --- src/PubSubClient.cpp | 4 ++++ src/PubSubClient.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 5932bdb..473bff1 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -376,6 +376,10 @@ boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigne return false; } +boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) { + return publish_P(topic, (const uint8_t*)payload, strlen(payload), retained); +} + boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { uint8_t llen = 0; uint8_t digit; diff --git a/src/PubSubClient.h b/src/PubSubClient.h index be4bd67..f5473ed 100755 --- a/src/PubSubClient.h +++ b/src/PubSubClient.h @@ -131,6 +131,7 @@ public: boolean publish(const char* topic, const char* payload, boolean retained); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + boolean publish_P(const char* topic, const char* payload, boolean retained); boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); boolean subscribe(const char* topic); boolean subscribe(const char* topic, uint8_t qos); From bb101c58e8c1bd970f3b6cde58e31d77a8c0d56a Mon Sep 17 00:00:00 2001 From: Rotzbua Date: Sat, 20 Jan 2018 01:13:10 +0100 Subject: [PATCH 02/20] Update link http->https (#384) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8317691..572b4a5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ a server that supports MQTT. The library comes with a number of example sketches. See File > Examples > PubSubClient within the Arduino application. -Full API documentation is available here: http://pubsubclient.knolleary.net +Full API documentation is available here: https://pubsubclient.knolleary.net ## Limitations From f029640ee6365c58ebfe66ebf6bf1733d322f2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trygve=20Laugst=C3=B8l?= Date: Fri, 23 Feb 2018 03:29:44 +0100 Subject: [PATCH 03/20] Fixing compiler warnings exposed with -Wall and -Wextra: qos can't be less than zero. (#274) --- src/PubSubClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 5932bdb..603fcc8 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -468,7 +468,7 @@ boolean PubSubClient::subscribe(const char* topic) { } boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { - if (qos < 0 || qos > 1) { + if (qos > 1) { return false; } if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { From 54be6e87db16d7dbc60d4c4674566ec08b0115c2 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 18 Jul 2018 11:02:08 +0100 Subject: [PATCH 04/20] Check remaining-length encoding is valid --- src/PubSubClient.cpp | 9 +++++++++ tests/src/receive_spec.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 603fcc8..29fbbfa 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -240,6 +240,12 @@ uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { uint8_t start = 0; do { + if (len == 6) { + // Invalid remaining length encoding - kill the connection + _state = MQTT_DISCONNECTED; + _client->stop(); + return 0; + } if(!readByte(&digit)) return 0; buffer[len++] = digit; length += (digit & 127) * multiplier; @@ -335,6 +341,9 @@ boolean PubSubClient::loop() { } else if (type == MQTTPINGRESP) { pingOutstanding = false; } + } else if (!connected()) { + // readPacket has closed the connection + return false; } } return true; diff --git a/tests/src/receive_spec.cpp b/tests/src/receive_spec.cpp index 54a62ee..4ecd439 100644 --- a/tests/src/receive_spec.cpp +++ b/tests/src/receive_spec.cpp @@ -160,6 +160,35 @@ int test_receive_oversized_message() { END_IT } +int test_drop_invalid_remaining_length_message() { + IT("drops invalid remaining length message"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x92,0x92,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.respond(publish,21); + + rc = client.loop(); + + IS_FALSE(rc); + + IS_FALSE(callback_called); + + IS_FALSE(shimClient.error()); + + END_IT +} + + int test_receive_oversized_stream_message() { IT("drops an oversized message"); reset_callback(); @@ -241,6 +270,7 @@ int main() test_receive_callback(); test_receive_stream(); test_receive_max_sized_message(); + test_drop_invalid_remaining_length_message(); test_receive_oversized_message(); test_receive_oversized_stream_message(); test_receive_qos1(); From 9e1a6e64799218f5eb3609f7097357c6e8314937 Mon Sep 17 00:00:00 2001 From: apicquot Date: Tue, 7 Aug 2018 17:29:29 -0400 Subject: [PATCH 05/20] Update PubSubClient.cpp added yield to resolve random connection failure --- src/PubSubClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 29fbbfa..c7afc6a 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -209,6 +209,7 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass boolean PubSubClient::readByte(uint8_t * result) { uint32_t previousMillis = millis(); while(!_client->available()) { + yield(); uint32_t currentMillis = millis(); if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){ return false; From 3b3a8da8d25f0d9c21e0699c1edf4e355d53f177 Mon Sep 17 00:00:00 2001 From: Adrian McEwen Date: Wed, 15 Aug 2018 17:15:04 +0100 Subject: [PATCH 06/20] Add large-payload API, make max header size a define, not magic number. --- keywords.txt | 3 ++ src/PubSubClient.cpp | 69 ++++++++++++++++++++++++++++++++++---------- src/PubSubClient.h | 25 ++++++++++++++++ 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/keywords.txt b/keywords.txt index b979588..1ee23d0 100755 --- a/keywords.txt +++ b/keywords.txt @@ -16,6 +16,9 @@ connect KEYWORD2 disconnect KEYWORD2 publish KEYWORD2 publish_P KEYWORD2 +beginPublish KEYWORD2 +endPublish KEYWORD2 +write KEYWORD2 subscribe KEYWORD2 unsubscribe KEYWORD2 loop KEYWORD2 diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 29fbbfa..f60d6a0 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -125,7 +125,7 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass if (result == 1) { nextMsgId = 1; // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; unsigned int j; #if MQTT_VERSION == MQTT_VERSION_3_1 @@ -171,7 +171,7 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass } } - write(MQTTCONNECT,buffer,length-5); + write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE); lastInActivity = lastOutActivity = millis(); @@ -365,12 +365,12 @@ boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigne boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { if (connected()) { - if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) { + if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) { // Too long return false; } // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; length = writeString(topic,buffer,length); uint16_t i; for (i=0;iwrite(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen)); + lastOutActivity = millis(); + return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen))); + } + return false; +} + +int PubSubClient::endPublish() { + return 1; +} + +size_t PubSubClient::write(uint8_t data) { + lastOutActivity = millis(); + return _client->write(data); +} + +size_t PubSubClient::write(const uint8_t *buffer, size_t size) { + lastOutActivity = millis(); + return _client->write(buffer,size); +} + +size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) { uint8_t lenBuf[4]; uint8_t llen = 0; uint8_t digit; uint8_t pos = 0; - uint16_t rc; uint16_t len = length; do { digit = len % 128; @@ -449,12 +480,18 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { buf[4-llen] = header; for (int i=0;i 0) && result) { @@ -466,9 +503,9 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { } return result; #else - rc = _client->write(buf+(4-llen),length+1+llen); + rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen); lastOutActivity = millis(); - return (rc == 1+llen+length); + return (rc == hlen+length); #endif } @@ -486,7 +523,7 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { } if (connected()) { // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; nextMsgId++; if (nextMsgId == 0) { nextMsgId = 1; @@ -495,7 +532,7 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { buffer[length++] = (nextMsgId & 0xFF); length = writeString((char*)topic, buffer,length); buffer[length++] = qos; - return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5); + return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } @@ -506,7 +543,7 @@ boolean PubSubClient::unsubscribe(const char* topic) { return false; } if (connected()) { - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; nextMsgId++; if (nextMsgId == 0) { nextMsgId = 1; @@ -514,7 +551,7 @@ boolean PubSubClient::unsubscribe(const char* topic) { buffer[length++] = (nextMsgId >> 8); buffer[length++] = (nextMsgId & 0xFF); length = writeString(topic, buffer,length); - return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5); + return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } diff --git a/src/PubSubClient.h b/src/PubSubClient.h index be4bd67..dc81ff3 100755 --- a/src/PubSubClient.h +++ b/src/PubSubClient.h @@ -73,6 +73,9 @@ #define MQTTQOS1 (1 << 1) #define MQTTQOS2 (2 << 1) +// Maximum size of fixed header and variable length size header +#define MQTT_MAX_HEADER_SIZE 5 + #ifdef ESP8266 #include #define MQTT_CALLBACK_SIGNATURE std::function callback @@ -94,6 +97,11 @@ private: boolean readByte(uint8_t * result, uint16_t * index); boolean write(uint8_t header, uint8_t* buf, uint16_t length); uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); + // Build up the header ready to send + // Returns the size of the header + // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start + // (MQTT_MAX_HEADER_SIZE - ) bytes into the buffer + size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length); IPAddress ip; const char* domain; uint16_t port; @@ -132,6 +140,23 @@ public: boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + // Start to publish a message. + // This API: + // beginPublish(...) + // one or more calls to write(...) + // endPublish() + // Allows for arbitrarily large payloads to be sent without them having to be copied into + // a new buffer and held in memory at one time + // Returns 1 if the message was started successfully, 0 if there was an error + boolean beginPublish(const char* topic, unsigned int plength, boolean retained); + // Finish off this publish message (started with beginPublish) + // Returns 1 if the packet was sent successfully, 0 if there was an error + int endPublish(); + // Write a single byte of payload (only to be used with beginPublish/endPublish) + size_t write(uint8_t); + // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish) + // Returns the number of bytes written + size_t write(const uint8_t *buffer, size_t size); boolean subscribe(const char* topic); boolean subscribe(const char* topic, uint8_t qos); boolean unsubscribe(const char* topic); From c87c9a47b3d4222c4d853e1737c8329269bb1fc7 Mon Sep 17 00:00:00 2001 From: bwibwi13 Date: Sun, 19 Aug 2018 08:39:39 +0200 Subject: [PATCH 07/20] Correct maximum size of snprintf (use same size as the buffer ;) --- examples/mqtt_esp8266/mqtt_esp8266.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mqtt_esp8266/mqtt_esp8266.ino b/examples/mqtt_esp8266/mqtt_esp8266.ino index 34333c9..e07c70d 100644 --- a/examples/mqtt_esp8266/mqtt_esp8266.ino +++ b/examples/mqtt_esp8266/mqtt_esp8266.ino @@ -124,7 +124,7 @@ void loop() { if (now - lastMsg > 2000) { lastMsg = now; ++value; - snprintf (msg, 75, "hello world #%ld", value); + snprintf (msg, 50, "hello world #%ld", value); Serial.print("Publish message: "); Serial.println(msg); client.publish("outTopic", msg); From 0c2d12d8b04a866cff04c94fa78929649a3c4521 Mon Sep 17 00:00:00 2001 From: Adrian McEwen Date: Tue, 4 Sep 2018 13:31:43 +0100 Subject: [PATCH 08/20] Allow the large message publishing to use all the `print` methods. --- src/PubSubClient.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PubSubClient.h b/src/PubSubClient.h index dc81ff3..4d80839 100755 --- a/src/PubSubClient.h +++ b/src/PubSubClient.h @@ -83,7 +83,7 @@ #define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int) #endif -class PubSubClient { +class PubSubClient : public Print { private: Client* _client; uint8_t buffer[MQTT_MAX_PACKET_SIZE]; @@ -153,10 +153,10 @@ public: // Returns 1 if the packet was sent successfully, 0 if there was an error int endPublish(); // Write a single byte of payload (only to be used with beginPublish/endPublish) - size_t write(uint8_t); + virtual size_t write(uint8_t); // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish) // Returns the number of bytes written - size_t write(const uint8_t *buffer, size_t size); + virtual size_t write(const uint8_t *buffer, size_t size); boolean subscribe(const char* topic); boolean subscribe(const char* topic, uint8_t qos); boolean unsubscribe(const char* topic); From b2f3a6d2ec5a31c0f3036d3a94a19a6619e3401d Mon Sep 17 00:00:00 2001 From: Adrian McEwen Date: Tue, 4 Sep 2018 13:36:45 +0100 Subject: [PATCH 09/20] Add example code for using the large message API. --- .../mqtt_large_message/mqtt_large_message.ino | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 examples/mqtt_large_message/mqtt_large_message.ino diff --git a/examples/mqtt_large_message/mqtt_large_message.ino b/examples/mqtt_large_message/mqtt_large_message.ino new file mode 100644 index 0000000..e048c3e --- /dev/null +++ b/examples/mqtt_large_message/mqtt_large_message.ino @@ -0,0 +1,179 @@ +/* + Long message ESP8266 MQTT example + + This sketch demonstrates sending arbitrarily large messages in combination + with the ESP8266 board/library. + + It connects to an MQTT server then: + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "greenBottles/#", printing out any messages + it receives. NB - it assumes the received payloads are strings not binary + - If the sub-topic is a number, it publishes a "greenBottles/lyrics" message + with a payload consisting of the lyrics to "10 green bottles", replacing + 10 with the number given in the sub-topic. + + It will reconnect to the server if the connection is lost using a blocking + reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to + achieve the same result without blocking the main loop. + + To install the ESP8266 board, (using Arduino 1.6.4+): + - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": + http://arduino.esp8266.com/stable/package_esp8266com_index.json + - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" + - Select your ESP8266 in "Tools -> Board" + +*/ + +#include +#include + +// Update these with values suitable for your network. + +const char* ssid = "........"; +const char* password = "........"; +const char* mqtt_server = "broker.mqtt-dashboard.com"; + +WiFiClient espClient; +PubSubClient client(espClient); +long lastMsg = 0; +char msg[50]; +int value = 0; + +void setup_wifi() { + + delay(10); + // We start by connecting to a WiFi network + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + randomSeed(micros()); + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); +} + +void callback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i = 0; i < length; i++) { + Serial.print((char)payload[i]); + } + Serial.println(); + + // Find out how many bottles we should generate lyrics for + String topicStr(topic); + int bottleCount = 0; // assume no bottles unless we correctly parse a value from the topic + if (topicStr.indexOf('/') >= 0) { + // The topic includes a '/', we'll try to read the number of bottles from just after that + topicStr.remove(0, topicStr.indexOf('/')+1); + // Now see if there's a number of bottles after the '/' + bottleCount = topicStr.toInt(); + } + + if (bottleCount > 0) { + // Work out how big our resulting message will be + int msgLen = 0; + for (int i = bottleCount; i > 0; i--) { + String numBottles(i); + msgLen += 2*numBottles.length(); + if (i == 1) { + msgLen += 2*String(" green bottle, standing on the wall\n").length(); + } else { + msgLen += 2*String(" green bottles, standing on the wall\n").length(); + } + msgLen += String("And if one green bottle should accidentally fall\nThere'll be ").length(); + switch (i) { + case 1: + msgLen += String("no green bottles, standing on the wall\n\n").length(); + break; + case 2: + msgLen += String("1 green bottle, standing on the wall\n\n").length(); + break; + default: + numBottles = i-1; + msgLen += numBottles.length(); + msgLen += String(" green bottles, standing on the wall\n\n").length(); + break; + }; + } + + // Now we can start to publish the message + client.beginPublish("greenBottles/lyrics", msgLen, false); + for (int i = bottleCount; i > 0; i--) { + for (int j = 0; j < 2; j++) { + client.print(i); + if (i == 1) { + client.print(" green bottle, standing on the wall\n"); + } else { + client.print(" green bottles, standing on the wall\n"); + } + } + client.print("And if one green bottle should accidentally fall\nThere'll be "); + switch (i) { + case 1: + client.print("no green bottles, standing on the wall\n\n"); + break; + case 2: + client.print("1 green bottle, standing on the wall\n\n"); + break; + default: + client.print(i-1); + client.print(" green bottles, standing on the wall\n\n"); + break; + }; + } + // Now we're done! + client.endPublish(); + } +} + +void reconnect() { + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + // Attempt to connect + if (client.connect(clientId.c_str())) { + Serial.println("connected"); + // Once connected, publish an announcement... + client.publish("outTopic", "hello world"); + // ... and resubscribe + client.subscribe("greenBottles/#"); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() { + pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output + Serial.begin(115200); + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + +void loop() { + + if (!client.connected()) { + reconnect(); + } + client.loop(); +} From 0e2d6c322bba946ea9fc3a4e5d2d0dfe9070db3b Mon Sep 17 00:00:00 2001 From: Folkert van Heusden Date: Fri, 7 Sep 2018 21:39:45 +0200 Subject: [PATCH 10/20] Update PubSubClient.cpp Make sure all data is flushed to the other end when doing a disconnect(): that way we know for sure that it is there when we disconnect the wifi or maybe even reboot. This change was made after I noticed that I did not get any mqtt messages. I verified that it indeed solves the problem. Example code on request. --- src/PubSubClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 29fbbfa..3c5c5f7 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -524,6 +524,7 @@ void PubSubClient::disconnect() { buffer[1] = 0; _client->write(buffer,2); _state = MQTT_DISCONNECTED; + _client->flush(); _client->stop(); lastInActivity = lastOutActivity = millis(); } From a1cfd5af5643838392fbaa3a3c2b816e5745da27 Mon Sep 17 00:00:00 2001 From: Nikhil Warke Date: Mon, 24 Sep 2018 00:29:11 +0530 Subject: [PATCH 11/20] Fix spelling in mqtt_esp8266 example --- examples/mqtt_esp8266/mqtt_esp8266.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mqtt_esp8266/mqtt_esp8266.ino b/examples/mqtt_esp8266/mqtt_esp8266.ino index 34333c9..60ff938 100644 --- a/examples/mqtt_esp8266/mqtt_esp8266.ino +++ b/examples/mqtt_esp8266/mqtt_esp8266.ino @@ -74,7 +74,7 @@ void callback(char* topic, byte* payload, unsigned int length) { if ((char)payload[0] == '1') { digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level // but actually the LED is on; this is because - // it is acive low on the ESP-01) + // it is active low on the ESP-01) } else { digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH } From 0e8e3123cff80ce8e927adf6a45e4d868ac16a97 Mon Sep 17 00:00:00 2001 From: Maxim Kukushkin Date: Tue, 2 Oct 2018 00:02:31 +0100 Subject: [PATCH 12/20] Added support of CleanSession flag during connect --- src/PubSubClient.cpp | 13 ++++++++----- src/PubSubClient.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 29fbbfa..a8c0560 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -102,18 +102,18 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN } boolean PubSubClient::connect(const char *id) { - return connect(id,NULL,NULL,0,0,0,0); + return connect(id,NULL,NULL,0,0,0,0,1); } boolean PubSubClient::connect(const char *id, const char *user, const char *pass) { - return connect(id,user,pass,0,0,0,0); + return connect(id,user,pass,0,0,0,0,1); } boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { - return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage); + return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1); } -boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { +boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) { if (!connected()) { int result = 0; @@ -143,9 +143,12 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass if (willTopic) { v = 0x06|(willQos<<3)|(willRetain<<5); } else { - v = 0x02; + v = 0; } + if (cleanSession) + v = v|0x02; + if(user != NULL) { v = v|0x80; diff --git a/src/PubSubClient.h b/src/PubSubClient.h index be4bd67..97f92ec 100755 --- a/src/PubSubClient.h +++ b/src/PubSubClient.h @@ -125,7 +125,7 @@ public: boolean connect(const char* id); boolean connect(const char* id, const char* user, const char* pass); boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); - boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession); void disconnect(); boolean publish(const char* topic, const char* payload); boolean publish(const char* topic, const char* payload, boolean retained); From 8154cbc09cdf72258d9f1783f308fca9cc876fde Mon Sep 17 00:00:00 2001 From: Maxim Kukushkin Date: Mon, 22 Oct 2018 23:35:47 +0100 Subject: [PATCH 13/20] Fixed an issue with clean session flag set when willTopic is provided --- src/PubSubClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index a8c0560..97ae909 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -141,7 +141,7 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass uint8_t v; if (willTopic) { - v = 0x06|(willQos<<3)|(willRetain<<5); + v = 0x04|(willQos<<3)|(willRetain<<5); } else { v = 0; } From 4ecd32ec0898783b3392c1471287a1b7b6f9bbed Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 1 Nov 2018 23:25:04 +0000 Subject: [PATCH 14/20] Fix up CI tests --- tests/src/lib/Arduino.h | 1 + tests/src/lib/Print.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/src/lib/Print.h diff --git a/tests/src/lib/Arduino.h b/tests/src/lib/Arduino.h index c675280..9bb814e 100644 --- a/tests/src/lib/Arduino.h +++ b/tests/src/lib/Arduino.h @@ -5,6 +5,7 @@ #include #include #include +#include "Print.h" extern "C"{ diff --git a/tests/src/lib/Print.h b/tests/src/lib/Print.h new file mode 100644 index 0000000..02ef77c --- /dev/null +++ b/tests/src/lib/Print.h @@ -0,0 +1,28 @@ +/* + Print.h - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. + + 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 Print_h +#define Print_h + +class Print { + public: + virtual size_t write(uint8_t) = 0; +}; + +#endif From 3637cbec69d0d606d020c16991d3b9a6663084b7 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 1 Nov 2018 23:35:31 +0000 Subject: [PATCH 15/20] Revert "Added support of CleanSession flag during connect" --- src/PubSubClient.cpp | 15 ++++++--------- src/PubSubClient.h | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 633eeec..836484c 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -102,18 +102,18 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN } boolean PubSubClient::connect(const char *id) { - return connect(id,NULL,NULL,0,0,0,0,1); + return connect(id,NULL,NULL,0,0,0,0); } boolean PubSubClient::connect(const char *id, const char *user, const char *pass) { - return connect(id,user,pass,0,0,0,0,1); + return connect(id,user,pass,0,0,0,0); } boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { - return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1); + return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage); } -boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) { +boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { if (!connected()) { int result = 0; @@ -141,14 +141,11 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass uint8_t v; if (willTopic) { - v = 0x04|(willQos<<3)|(willRetain<<5); + v = 0x06|(willQos<<3)|(willRetain<<5); } else { - v = 0; + v = 0x02; } - if (cleanSession) - v = v|0x02; - if(user != NULL) { v = v|0x80; diff --git a/src/PubSubClient.h b/src/PubSubClient.h index 1057f1f..4d80839 100755 --- a/src/PubSubClient.h +++ b/src/PubSubClient.h @@ -133,7 +133,7 @@ public: boolean connect(const char* id); boolean connect(const char* id, const char* user, const char* pass); boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); - boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession); + boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); void disconnect(); boolean publish(const char* topic, const char* payload); boolean publish(const char* topic, const char* payload, boolean retained); From a0f09681f5a92189ffda995524b0f394fd11b30c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 1 Nov 2018 23:46:09 +0000 Subject: [PATCH 16/20] Add separate connect function for clean session + test --- src/PubSubClient.cpp | 4 +++ src/PubSubClient.h | 1 + tests/src/connect_spec.cpp | 53 +++++++++++++++++++++++++++++--------- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 97ae909..cbe09ba 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -113,6 +113,10 @@ boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t wil return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1); } +boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { + return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1); +} + boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) { if (!connected()) { int result = 0; diff --git a/src/PubSubClient.h b/src/PubSubClient.h index 97f92ec..62c915f 100755 --- a/src/PubSubClient.h +++ b/src/PubSubClient.h @@ -125,6 +125,7 @@ public: boolean connect(const char* id); boolean connect(const char* id, const char* user, const char* pass); boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession); void disconnect(); boolean publish(const char* topic, const char* payload); diff --git a/tests/src/connect_spec.cpp b/tests/src/connect_spec.cpp index 3c46e0c..202b2b9 100644 --- a/tests/src/connect_spec.cpp +++ b/tests/src/connect_spec.cpp @@ -98,6 +98,33 @@ int test_connect_fails_on_bad_rc() { END_IT } +int test_connect_non_clean_session() { + IT("sends a properly formatted non-clean session connect packet and succeeds"); + ShimClient shimClient; + + shimClient.setAllowConnect(true); + byte expectServer[] = { 172, 16, 0, 2 }; + shimClient.expectConnect(expectServer,1883); + byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x0,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + + shimClient.expect(connect,26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int state = client.state(); + IS_TRUE(state == MQTT_DISCONNECTED); + + int rc = client.connect((char*)"client_test1",0,0,0,0,0,0,0); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + state = client.state(); + IS_TRUE(state == MQTT_CONNECTED); + + END_IT +} + int test_connect_accepts_username_password() { IT("accepts a username and password"); ShimClient shimClient; @@ -256,18 +283,20 @@ int test_connect_disconnect_connect() { int main() { SUITE("Connect"); - test_connect_fails_no_network(); - test_connect_fails_on_no_response(); + test_connect_non_clean_session(); - test_connect_properly_formatted(); - test_connect_accepts_username_password(); - test_connect_fails_on_bad_rc(); - test_connect_properly_formatted_hostname(); - - test_connect_accepts_username_no_password(); - test_connect_ignores_password_no_username(); - test_connect_with_will(); - test_connect_with_will_username_password(); - test_connect_disconnect_connect(); + // test_connect_fails_no_network(); + // test_connect_fails_on_no_response(); + // + // test_connect_properly_formatted(); + // test_connect_accepts_username_password(); + // test_connect_fails_on_bad_rc(); + // test_connect_properly_formatted_hostname(); + // + // test_connect_accepts_username_no_password(); + // test_connect_ignores_password_no_username(); + // test_connect_with_will(); + // test_connect_with_will_username_password(); + // test_connect_disconnect_connect(); FINISH } From 4daba0ae5c11cca4da2fd98a1ba4fe0b490a4a86 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 2 Nov 2018 00:06:32 +0000 Subject: [PATCH 17/20] Fix remaining length protection --- src/PubSubClient.cpp | 2 +- tests/src/receive_spec.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index bd2514f..997bfb4 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -244,7 +244,7 @@ uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { uint8_t start = 0; do { - if (len == 6) { + if (len == 5) { // Invalid remaining length encoding - kill the connection _state = MQTT_DISCONNECTED; _client->stop(); diff --git a/tests/src/receive_spec.cpp b/tests/src/receive_spec.cpp index 4ecd439..9a18af0 100644 --- a/tests/src/receive_spec.cpp +++ b/tests/src/receive_spec.cpp @@ -174,8 +174,8 @@ int test_drop_invalid_remaining_length_message() { int rc = client.connect((char*)"client_test1"); IS_TRUE(rc); - byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x92,0x92,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; - shimClient.respond(publish,21); + byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x01,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.respond(publish,20); rc = client.loop(); From ee30733e247676ce7424938db79d6a91ff4d2750 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 2 Nov 2018 00:12:44 +0000 Subject: [PATCH 18/20] Fixup bad revert of 500 --- src/PubSubClient.cpp | 13 ++++++++----- tests/src/connect_spec.cpp | 28 ++++++++++++++-------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 997bfb4..022f90e 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -102,15 +102,15 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN } boolean PubSubClient::connect(const char *id) { - return connect(id,NULL,NULL,0,0,0,0); + return connect(id,NULL,NULL,0,0,0,0,1); } boolean PubSubClient::connect(const char *id, const char *user, const char *pass) { - return connect(id,user,pass,0,0,0,0); + return connect(id,user,pass,0,0,0,0,1); } boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { - return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage); + return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1); } boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { @@ -145,9 +145,12 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass uint8_t v; if (willTopic) { - v = 0x06|(willQos<<3)|(willRetain<<5); + v = 0x04|(willQos<<3)|(willRetain<<5); } else { - v = 0x02; + v = 0x00; + } + if (cleanSession) { + v = v|0x02; } if(user != NULL) { diff --git a/tests/src/connect_spec.cpp b/tests/src/connect_spec.cpp index 202b2b9..e27a1f5 100644 --- a/tests/src/connect_spec.cpp +++ b/tests/src/connect_spec.cpp @@ -283,20 +283,20 @@ int test_connect_disconnect_connect() { int main() { SUITE("Connect"); - test_connect_non_clean_session(); - // test_connect_fails_no_network(); - // test_connect_fails_on_no_response(); - // - // test_connect_properly_formatted(); - // test_connect_accepts_username_password(); - // test_connect_fails_on_bad_rc(); - // test_connect_properly_formatted_hostname(); - // - // test_connect_accepts_username_no_password(); - // test_connect_ignores_password_no_username(); - // test_connect_with_will(); - // test_connect_with_will_username_password(); - // test_connect_disconnect_connect(); + test_connect_fails_no_network(); + test_connect_fails_on_no_response(); + + test_connect_properly_formatted(); + test_connect_non_clean_session(); + test_connect_accepts_username_password(); + test_connect_fails_on_bad_rc(); + test_connect_properly_formatted_hostname(); + + test_connect_accepts_username_no_password(); + test_connect_ignores_password_no_username(); + test_connect_with_will(); + test_connect_with_will_username_password(); + test_connect_disconnect_connect(); FINISH } From 2d053d2df06f9bb864dc462d92453d3c1292796d Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 2 Nov 2018 00:50:52 +0000 Subject: [PATCH 19/20] Add buffer overflow protection to connect Closes #492 --- src/PubSubClient.cpp | 6 ++++++ src/PubSubClient.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/PubSubClient.cpp b/src/PubSubClient.cpp index 022f90e..48ec1ce 100755 --- a/src/PubSubClient.cpp +++ b/src/PubSubClient.cpp @@ -165,15 +165,21 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass buffer[length++] = ((MQTT_KEEPALIVE) >> 8); buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); + + CHECK_STRING_LENGTH(length,id) length = writeString(id,buffer,length); if (willTopic) { + CHECK_STRING_LENGTH(length,willTopic) length = writeString(willTopic,buffer,length); + CHECK_STRING_LENGTH(length,willMessage) length = writeString(willMessage,buffer,length); } if(user != NULL) { + CHECK_STRING_LENGTH(length,user) length = writeString(user,buffer,length); if(pass != NULL) { + CHECK_STRING_LENGTH(length,pass) length = writeString(pass,buffer,length); } } diff --git a/src/PubSubClient.h b/src/PubSubClient.h index 8fd8707..b476b63 100755 --- a/src/PubSubClient.h +++ b/src/PubSubClient.h @@ -83,6 +83,8 @@ #define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int) #endif +#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;} + class PubSubClient : public Print { private: Client* _client; From b3817289988abbef58336955487987c712f20618 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 2 Nov 2018 01:05:02 +0000 Subject: [PATCH 20/20] Add yield to mock test framework --- tests/src/lib/Arduino.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/src/lib/Arduino.h b/tests/src/lib/Arduino.h index 9bb814e..2a00f24 100644 --- a/tests/src/lib/Arduino.h +++ b/tests/src/lib/Arduino.h @@ -21,4 +21,6 @@ extern "C"{ #define PROGMEM #define pgm_read_byte_near(x) *(x) +#define yield(x) {} + #endif // Arduino_h