summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--examples/mqtt/consolepubsub/configuration.h307
-rw-r--r--examples/mqtt/consolepubsub/consolepubsub.pro6
-rw-r--r--examples/mqtt/consolepubsub/main_pub.cpp112
-rw-r--r--examples/mqtt/consolepubsub/main_sub.cpp112
-rw-r--r--examples/mqtt/consolepubsub/qtmqtt_pub.pro25
-rw-r--r--examples/mqtt/consolepubsub/qtmqtt_sub.pro25
-rw-r--r--examples/mqtt/mqtt.pro1
-rw-r--r--src/mqtt/qmqttclient.cpp3
-rw-r--r--src/mqtt/qmqttconnection.cpp12
-rw-r--r--src/mqtt/qmqttconnection_p.h1
-rw-r--r--tests/libfuzzer/mqtt/data_receive/data_receive.pro8
-rw-r--r--tests/libfuzzer/mqtt/data_receive/main.cpp145
13 files changed, 758 insertions, 1 deletions
diff --git a/.qmake.conf b/.qmake.conf
index a3c853d..7b49e2c 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,4 @@
load(qt_build_config)
CONFIG += warning_clean
-MODULE_VERSION = 5.12.3
+MODULE_VERSION = 5.13.0
diff --git a/examples/mqtt/consolepubsub/configuration.h b/examples/mqtt/consolepubsub/configuration.h
new file mode 100644
index 0000000..b25920f
--- /dev/null
+++ b/examples/mqtt/consolepubsub/configuration.h
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QByteArray>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QLoggingCategory>
+#include <QString>
+#include <QMqttClient>
+#include <QSslSocket>
+
+struct Configuration
+{
+ QString topic;
+ QByteArray content;
+ quint8 qos;
+ bool retain{false};
+ bool useEncryption{false};
+};
+
+QMqttClient *createClientWithConfiguration(QCoreApplication *app,
+ Configuration *msg,
+ bool publish)
+{
+ QCommandLineParser parser;
+ if (publish)
+ parser.setApplicationDescription("Qt MQTT publish tool");
+ else
+ parser.setApplicationDescription("Qt MQTT subscription tool");
+
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ QCommandLineOption optionDebug("d",
+ QLatin1String("Enable debug messages / logging categories"));
+ parser.addOption(optionDebug);
+
+ QCommandLineOption optionFile("f",
+ QLatin1String("Specify the content of a file as message."),
+ QLatin1String("filename"));
+ if (publish)
+ parser.addOption(optionFile);
+
+ QCommandLineOption optionClientId("i",
+ QLatin1String("Specify a client ID. Defaults to random value."),
+ QLatin1String("clientid"));
+ parser.addOption(optionClientId);
+
+ QCommandLineOption optionKeepAlive("k",
+ QLatin1String("Specify the keep-alive value in seconds."),
+ QLatin1String("keepAlive"));
+ parser.addOption(optionKeepAlive);
+
+ QCommandLineOption optionMessageContent("m",
+ QLatin1String("Specify the message content. Defaults to"
+ " a null message."),
+ QLatin1String("messageContent"));
+ if (publish)
+ parser.addOption(optionMessageContent);
+
+ QCommandLineOption optionPassword("P",
+ QLatin1String("Provide a password."),
+ QLatin1String("password"));
+ parser.addOption(optionPassword);
+
+ QCommandLineOption optionPort("p",
+ QLatin1String("Network port to connect to. Defaults to 1883."),
+ QLatin1String("hostPort"),
+ QLatin1String("1883"));
+ parser.addOption(optionPort);
+
+ QCommandLineOption optionQos("q",
+ QLatin1String("Quality of service level to use for all messages."
+ "Defaults to 0."),
+ QLatin1String("qos"),
+ QLatin1String("0"));
+ parser.addOption(optionQos);
+
+ QCommandLineOption optionRetain("r",
+ QLatin1String("Specify the retain flag for a message."));
+ if (publish)
+ parser.addOption(optionRetain);
+
+ QCommandLineOption optionHost("s",
+ QLatin1String("MQTT server to connect to. Defaults to localhost."),
+ QLatin1String("hostName"),
+ QLatin1String("localhost"));
+ parser.addOption(optionHost);
+
+ QCommandLineOption optionMessageTopic("t",
+ QLatin1String("Specify the message topic."),
+ QLatin1String("messageTopic"));
+ parser.addOption(optionMessageTopic);
+
+ QCommandLineOption optionUser("u",
+ QLatin1String("Provide a username."),
+ QLatin1String("username"));
+ parser.addOption(optionUser);
+
+ QCommandLineOption optionVersion("V",
+ QLatin1String("Specify the protocol version. Options are "
+ "mqtt31, mqtt311, mqtt5. Defaults to mqtt311."),
+ QLatin1String("protocolVersion"),
+ QLatin1String("mqtt311"));
+ parser.addOption(optionVersion);
+
+ QCommandLineOption optionCaFile("cafile",
+ QLatin1String("Specify a file containing trusted CA "
+ "certificates to enable encrypted communication."),
+ QLatin1String("cafile"));
+ parser.addOption(optionCaFile);
+
+ QCommandLineOption optionCaPath("capath",
+ QLatin1String("Specify a directory containing trusted CA "
+ "certificates to enable encrypted communication."),
+ QLatin1String("capath"));
+ parser.addOption(optionCaPath);
+
+ parser.process(*app);
+
+ auto *client = new QMqttClient(app);
+
+ client->setHostname(parser.value(optionHost));
+ bool ok = true;
+ quint16 port = static_cast<quint16>(parser.value(optionPort).toInt(&ok));
+ if (!ok) {
+ qWarning() << "Invalid port specified:" << parser.value(optionPort);
+ return nullptr;
+ }
+ client->setPort(port);
+
+ if (parser.isSet(optionUser))
+ client->setUsername(parser.value(optionUser));
+ if (parser.isSet(optionPassword))
+ client->setPassword(parser.value(optionPassword));
+ if (parser.isSet(optionClientId))
+ client->setClientId(parser.value(optionClientId));
+ if (parser.isSet(optionVersion)) {
+ const QString version = parser.value(optionVersion);
+ if (version == QLatin1String("mqtt31"))
+ client->setProtocolVersion(QMqttClient::MQTT_3_1);
+ else if (version == QLatin1String("mqtt311"))
+ client->setProtocolVersion(QMqttClient::MQTT_3_1_1);
+ else if (version == QLatin1String("mqtt5"))
+ client->setProtocolVersion(QMqttClient::MQTT_5_0);
+ else {
+ qWarning() << "Invalid protocol version specified:" << version;
+ return nullptr;
+ }
+ }
+
+ if (parser.isSet(optionCaFile) || parser.isSet(optionCaPath)) {
+#ifdef QT_NO_SSL
+ qWarning() << "Qt has not been compiled with SSL support.";
+ return nullptr;
+#else
+ QList<QString> fileNames;
+ if (parser.isSet(optionCaFile))
+ fileNames.append(parser.value(optionCaFile));
+
+ if (parser.isSet(optionCaPath)) {
+ QFileInfo path(parser.value(optionCaPath));
+ if (!path.isDir()) {
+ qWarning() << "Specified capath is not a directory";
+ return nullptr;
+ }
+ auto entries = QDir(parser.value(optionCaPath)).entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
+ for (auto entry : entries)
+ fileNames.append(entry.absoluteFilePath());
+ }
+ if (fileNames.isEmpty()) {
+ qWarning() << "No certificate file found.";
+ return nullptr;
+ }
+
+ QList<QSslCertificate> defaultCerts;
+ for (auto it : fileNames) {
+ auto certificates = QSslCertificate::fromPath(it);
+ if (certificates.isEmpty() && parser.isSet(optionDebug))
+ qWarning() << "File " << it << " does not contain any certificates";
+ defaultCerts.append(certificates);
+ }
+ if (defaultCerts.isEmpty()) {
+ qWarning() << "No certificate could be loaded.";
+ return nullptr;
+ }
+
+ QSslSocket::addDefaultCaCertificates(defaultCerts);
+ msg->useEncryption = true;
+#endif
+ }
+
+ if (parser.isSet(optionDebug))
+ QLoggingCategory::setFilterRules(QLatin1String("qt.mqtt.*=true"));
+
+ msg->qos = static_cast<quint8>(parser.value(optionQos).toInt(&ok));
+ if (!ok || msg->qos > 2) {
+ qWarning() << "Invalid quality of service for message specified:" << msg->qos;
+ return nullptr;
+ }
+
+ if (parser.isSet(optionKeepAlive)) {
+ const quint16 keep = static_cast<quint16>(parser.value(optionKeepAlive).toUInt(&ok));
+ if (!ok) {
+ qWarning() << "Invalid keep alive value specified";
+ return nullptr;
+ }
+ client->setKeepAlive(keep);
+ }
+
+ if (publish) {
+ if (parser.isSet(optionFile) && parser.isSet(optionMessageContent)) {
+ qWarning() << "You cannot specify a file and a text as message.";
+ return nullptr;
+ }
+ if (parser.isSet(optionFile)) {
+ QFile file(parser.value(optionFile));
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "Could not open specified file for reading.";
+ return nullptr;
+ }
+ msg->content = file.readAll();
+ file.close();
+ }
+ if (parser.isSet(optionMessageContent))
+ msg->content = parser.value(optionMessageContent).toUtf8();
+ }
+
+ if (!parser.isSet(optionMessageTopic)) {
+ qWarning() << "You must specify a topic to publish a message.";
+ return nullptr;
+ }
+ msg->topic = parser.value(optionMessageTopic);
+
+ if (publish && !QMqttTopicName(msg->topic).isValid()) {
+ qWarning() << "The specified message topic is invalid.";
+ return nullptr;
+ }
+
+ if (!publish && !QMqttTopicFilter(msg->topic).isValid()) {
+ qWarning() << "The specified subscription topic is invalid.";
+ return nullptr;
+ }
+
+ if (publish && parser.isSet(optionRetain))
+ msg->retain = true;
+
+ // Output:
+ qInfo() << "Client configuration:";
+ qInfo() << " Host:" << client->hostname() << " Port:" << client->port()
+ << " Protocol:" << client->protocolVersion();
+ qInfo() << " Username:" << client->username() << " Password:" << !client->password().isEmpty();
+ qInfo() << " Client ID:" << client->clientId() << "Keep Alive:" << client->keepAlive();
+
+ return client;
+}
+
diff --git a/examples/mqtt/consolepubsub/consolepubsub.pro b/examples/mqtt/consolepubsub/consolepubsub.pro
new file mode 100644
index 0000000..6aee476
--- /dev/null
+++ b/examples/mqtt/consolepubsub/consolepubsub.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+
+SUBDIRS = \
+ qtmqtt_pub.pro \
+ qtmqtt_sub.pro
+
diff --git a/examples/mqtt/consolepubsub/main_pub.cpp b/examples/mqtt/consolepubsub/main_pub.cpp
new file mode 100644
index 0000000..b47fb8e
--- /dev/null
+++ b/examples/mqtt/consolepubsub/main_pub.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "configuration.h"
+
+#include <QCoreApplication>
+#include <QMqttClient>
+#include <QSslSocket>
+#include <QTimer>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+ QCoreApplication::setApplicationName(QStringLiteral("qtmqtt_pub"));
+ QCoreApplication::setApplicationVersion(QStringLiteral("1.0"));
+
+ // Create the client
+ Configuration description;
+ auto *client = createClientWithConfiguration(&a, &description, true);
+
+ if (!client)
+ return -1;
+
+ a.connect(client, &QMqttClient::errorChanged, [&client](const QMqttClient::ClientError e) {
+ if (e == QMqttClient::NoError)
+ return;
+
+ qWarning() << "Error Occurred:" << e << " Client state:" << client->state();
+ client->disconnectFromHost();
+ });
+
+ a.connect(client, &QMqttClient::messageSent, [&client] (quint32 id) {
+ qInfo() << "Message with ID:" << id << " sent";
+ client->disconnectFromHost();
+ });
+
+ a.connect(client, &QMqttClient::stateChanged, [&client] (QMqttClient::ClientState s) {
+ if (s == QMqttClient::Disconnected) {
+ client->deleteLater();
+ qApp->quit();
+ }
+ });
+
+ a.connect(client, &QMqttClient::connected, [&client, description]() {
+ qInfo() << "Message:";
+ qInfo() << " Topic:" << description.topic << " QoS:" << description.qos
+ << " Retain:" << description.retain;
+ qInfo() << " Content: " << description.content.left(50);
+ client->publish(description.topic,
+ description.content,
+ description.qos,
+ description.retain);
+ if (description.qos == 0)// 0 has no acknowledgment
+ QTimer::singleShot(500, client, &QMqttClient::disconnectFromHost);
+ });
+
+#ifndef QT_NO_SSL
+ if (description.useEncryption)
+ client->connectToHostEncrypted();
+ else
+#endif
+ client->connectToHost();
+
+ return a.exec();
+}
diff --git a/examples/mqtt/consolepubsub/main_sub.cpp b/examples/mqtt/consolepubsub/main_sub.cpp
new file mode 100644
index 0000000..9e94037
--- /dev/null
+++ b/examples/mqtt/consolepubsub/main_sub.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "configuration.h"
+
+#include <QCoreApplication>
+#include <QMqttClient>
+#include <QSslSocket>
+#include <QTimer>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+ QCoreApplication::setApplicationName(QStringLiteral("qtmqtt_sub"));
+ QCoreApplication::setApplicationVersion(QStringLiteral("1.0"));
+
+ // Create the client
+ Configuration description;
+ auto *client = createClientWithConfiguration(&a, &description, false);
+
+ if (!client)
+ return -1;
+
+ a.connect(client, &QMqttClient::errorChanged, [&client](const QMqttClient::ClientError e) {
+ if (e == QMqttClient::NoError)
+ return;
+
+ qWarning() << "Error Occurred:" << e << " Client state:" << client->state();
+ client->disconnectFromHost();
+ });
+
+ a.connect(client, &QMqttClient::stateChanged, [&client] (QMqttClient::ClientState s) {
+ if (s == QMqttClient::Disconnected) {
+ client->deleteLater();
+ qApp->quit();
+ }
+ });
+
+ a.connect(client, &QMqttClient::connected, [&client, description]() {
+ auto sub = client->subscribe(description.topic, description.qos);
+ client->connect(sub, &QMqttSubscription::stateChanged, [&client](QMqttSubscription::SubscriptionState s) {
+ qInfo() << "Subscription state:" << s;
+ if (s == QMqttSubscription::Unsubscribed)
+ client->disconnectFromHost();
+ });
+
+ client->connect(sub, &QMqttSubscription::messageReceived, [](const QMqttMessage &msg) {
+ qInfo() << "ID:" << msg.id()
+ << "Topic:" << msg.topic().name()
+ << "QoS:" << msg.qos()
+ << "Retain:" << msg.retain()
+ << "Duplicate:" << msg.duplicate()
+ << "Payload:" << msg.payload().left(50) << (msg.payload().size() > 50 ? "..." : "");
+ });
+ });
+
+#ifndef QT_NO_SSL
+ if (description.useEncryption)
+ client->connectToHostEncrypted();
+ else
+#endif
+ client->connectToHost();
+
+ return a.exec();
+}
diff --git a/examples/mqtt/consolepubsub/qtmqtt_pub.pro b/examples/mqtt/consolepubsub/qtmqtt_pub.pro
new file mode 100644
index 0000000..bfa84b7
--- /dev/null
+++ b/examples/mqtt/consolepubsub/qtmqtt_pub.pro
@@ -0,0 +1,25 @@
+QT -= gui
+QT += mqtt
+
+CONFIG += c++11 console
+CONFIG -= app_bundle
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ main_pub.cpp
+
+HEADERS += \
+ configuration.h
+
+target.path = $$[QT_INSTALL_EXAMPLES]/mqtt/consolepubsub
+INSTALLS += target
diff --git a/examples/mqtt/consolepubsub/qtmqtt_sub.pro b/examples/mqtt/consolepubsub/qtmqtt_sub.pro
new file mode 100644
index 0000000..c022650
--- /dev/null
+++ b/examples/mqtt/consolepubsub/qtmqtt_sub.pro
@@ -0,0 +1,25 @@
+QT -= gui
+QT += mqtt
+
+CONFIG += c++11 console
+CONFIG -= app_bundle
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+ main_sub.cpp
+
+HEADERS += \
+ configuration.h
+
+target.path = $$[QT_INSTALL_EXAMPLES]/mqtt/consolepubsub
+INSTALLS += target
diff --git a/examples/mqtt/mqtt.pro b/examples/mqtt/mqtt.pro
index c734248..db620d0 100644
--- a/examples/mqtt/mqtt.pro
+++ b/examples/mqtt/mqtt.pro
@@ -1,5 +1,6 @@
TEMPLATE = subdirs
SUBDIRS += \
+ consolepubsub \
simpleclient \
subscriptions
diff --git a/src/mqtt/qmqttclient.cpp b/src/mqtt/qmqttclient.cpp
index 0ea0f1e..c8fc73f 100644
--- a/src/mqtt/qmqttclient.cpp
+++ b/src/mqtt/qmqttclient.cpp
@@ -93,6 +93,9 @@ Q_LOGGING_CATEGORY(lcMqttClient, "qt.mqtt.client")
The interval is specified in milliseconds. However, most brokers are not
capable of using such a high granularity and will fall back to an interval
specified in seconds.
+
+ If the broker does not respond within a grace period the connection will be
+ closed.
*/
/*!
diff --git a/src/mqtt/qmqttconnection.cpp b/src/mqtt/qmqttconnection.cpp
index 61a6ef0..483e6f4 100644
--- a/src/mqtt/qmqttconnection.cpp
+++ b/src/mqtt/qmqttconnection.cpp
@@ -577,11 +577,19 @@ bool QMqttConnection::sendControlPingRequest()
if (m_internalState != QMqttConnection::BrokerConnected)
return false;
+ // 3.1.2.10 If a Client does not receive a PINGRESP packet within a reasonable amount of time
+ // after it has sent a PINGREQ, it SHOULD close the Network Connection to the Server
+ // Consider two pending PINGRESP as reasonable.
+ if (m_pingTimeout > 1) {
+ closeConnection(QMqttClient::ServerUnavailable);
+ return false;
+ }
const QMqttControlPacket packet(QMqttControlPacket::PINGREQ);
if (!writePacketToTransport(packet)) {
qCDebug(lcMqttConnection) << "Failed to write PINGREQ to transport.";
return false;
}
+ m_pingTimeout++;
return true;
}
@@ -590,6 +598,7 @@ bool QMqttConnection::sendControlDisconnect()
qCDebug(lcMqttConnection) << Q_FUNC_INFO;
m_pingTimer.stop();
+ m_pingTimeout = 0;
m_activeSubscriptions.clear();
@@ -675,6 +684,7 @@ void QMqttConnection::transportConnectionClosed()
m_readBuffer.clear();
m_readPosition = 0;
m_pingTimer.stop();
+ m_pingTimeout = 0;
if (m_internalState == BrokerDisconnected) // We manually disconnected
m_clientPrivate->setStateAndError(QMqttClient::Disconnected, QMqttClient::NoError);
else
@@ -733,6 +743,7 @@ void QMqttConnection::closeConnection(QMqttClient::ClientError error)
m_readBuffer.clear();
m_readPosition = 0;
m_pingTimer.stop();
+ m_pingTimeout = 0;
m_activeSubscriptions.clear();
m_internalState = BrokerDisconnected;
m_transport->disconnect();
@@ -1628,6 +1639,7 @@ void QMqttConnection::finalize_pingresp()
closeConnection(QMqttClient::ProtocolViolation);
return;
}
+ m_pingTimeout--;
emit m_clientPrivate->m_client->pingResponseReceived();
}
diff --git a/src/mqtt/qmqttconnection_p.h b/src/mqtt/qmqttconnection_p.h
index 84dc875..a14f914 100644
--- a/src/mqtt/qmqttconnection_p.h
+++ b/src/mqtt/qmqttconnection_p.h
@@ -154,6 +154,7 @@ private:
QMap<quint16, QSharedPointer<QMqttControlPacket>> m_pendingReleaseMessages;
InternalConnectionState m_internalState{BrokerDisconnected};
QTimer m_pingTimer;
+ int m_pingTimeout{0};
QVector<QMqttTopicName> m_receiveAliases;
QVector<QMqttTopicName> m_publishAliases;
diff --git a/tests/libfuzzer/mqtt/data_receive/data_receive.pro b/tests/libfuzzer/mqtt/data_receive/data_receive.pro
new file mode 100644
index 0000000..1b9e3bc
--- /dev/null
+++ b/tests/libfuzzer/mqtt/data_receive/data_receive.pro
@@ -0,0 +1,8 @@
+QT -= gui
+QT += mqtt testlib
+QT += mqtt-private
+
+SOURCES += \
+ main.cpp
+
+LIBS += -fsanitize=fuzzer
diff --git a/tests/libfuzzer/mqtt/data_receive/main.cpp b/tests/libfuzzer/mqtt/data_receive/main.cpp
new file mode 100644
index 0000000..307db64
--- /dev/null
+++ b/tests/libfuzzer/mqtt/data_receive/main.cpp
@@ -0,0 +1,145 @@
+/******************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtMqtt module.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+#include <QtMqtt/private/qmqttcontrolpacket_p.h>
+
+#include <QtMqtt>
+#include <QtTest>
+#include <QtCore>
+
+Q_DECLARE_METATYPE(QMqttSubscription::SubscriptionState)
+
+class FakeServer : public QIODevice
+{
+ Q_OBJECT
+public:
+ explicit FakeServer(QObject *parent);
+
+ qint64 readData(char *data, qint64 maxlen) override;
+ qint64 writeData(const char *data, qint64 len) override;
+
+ int m_messageState{0};
+ QByteArray m_readBuffer;
+};
+
+FakeServer::FakeServer(QObject *parent)
+ : QIODevice(parent)
+{
+ m_messageState = 0;
+ open(QIODevice::ReadWrite | QIODevice::Unbuffered);
+}
+
+qint64 FakeServer::readData(char *data, qint64 maxlen)
+{
+ const qint64 dataToWrite = qMax<qint64>(0, qMin<qint64>(maxlen, m_readBuffer.size()));
+ memcpy(data, m_readBuffer.constData(), static_cast<size_t>(dataToWrite));
+ m_readBuffer = m_readBuffer.mid(static_cast<int>(dataToWrite));
+ return dataToWrite;
+}
+
+void bufferAppend(QByteArray &buffer, quint16 value)
+{
+ const quint16 msb = qToBigEndian<quint16>(value);
+ const char * msb_c = reinterpret_cast<const char*>(&msb);
+ buffer.append(msb_c, 2);
+}
+
+qint64 FakeServer::writeData(const char *data, qint64 len)
+{
+ if (m_messageState == 0) { // Received CONNECT
+ QByteArray response;
+ response += 0x20;
+ response += quint8(2); // Payload size
+ response += char(0); // ackFlags
+ response += char(0); // result
+
+ m_readBuffer = response;
+ emit readyRead();
+ m_messageState = 1;
+ } else if (m_messageState == 1) { // Received SUBSCRIBE
+ // Byte 0 == 0x82 (0x80 SUBSCRIBE 0x02 standard)
+ quint8 msg = reinterpret_cast<const quint8 *>(data)[0];
+ if (msg != quint8(0x82))
+ qFatal("Expected subscribe message");
+ // Byte 1+2 == ID
+ const quint16 id_big = *reinterpret_cast<const quint16 *>(&data[2]);
+ const quint16 id = qFromBigEndian<quint16>(id_big);
+
+ QMqttControlPacket packet(QMqttControlPacket::SUBACK);
+ packet.append(id);
+ const quint8 qosLevel = 1;
+ packet.append(static_cast<char>(qosLevel));
+ m_readBuffer = packet.serialize();
+ // We need to be async to have QMqttConnection prepare its internals
+ QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
+
+ m_messageState = 2;
+ }
+ // Ignore any follow up data
+
+ return len;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const char *Data, size_t Size)
+{
+ static int argc = 1;
+ static char *argv_1[] = {qstrdup("fuzzprepare")};
+ static char **argv = argv_1;
+ static QCoreApplication a(argc, argv);
+
+ FakeServer serv(&a);
+ QMqttClient client;
+ client.setHostname(QLatin1String("localhost"));
+ client.setPort(1883);
+
+ client.setTransport(&serv, QMqttClient::IODevice);
+ client.connectToHost();
+
+ QSignalSpy spy(&client, &QMqttClient::connected);
+ spy.wait(5);
+
+ if (client.state() != QMqttClient::Connected) {
+ qFatal("Not able to run test propertly");
+ return -1;
+ }
+
+ auto sub = client.subscribe(QLatin1String("a"), 1);
+ qRegisterMetaType<QMqttSubscription::SubscriptionState>("SubscriptionState");
+ QSignalSpy subSpy(sub, &QMqttSubscription::stateChanged);
+ spy.wait(5);
+ if (sub->state() != QMqttSubscription::Subscribed)
+ spy.wait(10);
+ if (sub->state() != QMqttSubscription::Subscribed)
+ qFatal("Could not subscribe");
+
+ serv.m_readBuffer = QByteArray(Data, static_cast<int>(Size));
+ QMetaObject::invokeMethod(&serv, "readyRead");
+ QCoreApplication::processEvents();
+ return 0;
+}
+
+#include "main.moc"