Some fixes in Coap
* Fixed an indexOutOfRange warning when creating coap requests because the first addOption() call was calling m_options.insert(1) on an empty list. * Old code was appending multiple UDP datagrams to a single big Coap message, however, Coap is specified to only send s single datagram per message. The datagram length specifies the payload size. * some boolean member variables weren't initialized which resulted in occational wrong flags. * Parsing had issues with determining the option length in some occations and also would crash when receiving coap messages without any options or payload. To get rid of the complex and erraneous index calculations, the entire package parsing is now using a DataStream. (This makes it work with Shelly devices)
This commit is contained in:
parent
45081ad64e
commit
62644650c0
@ -801,16 +801,15 @@ void Coap::hostLookupFinished(const QHostInfo &hostInfo)
|
|||||||
void Coap::onReadyRead()
|
void Coap::onReadyRead()
|
||||||
{
|
{
|
||||||
QHostAddress hostAddress;
|
QHostAddress hostAddress;
|
||||||
QByteArray data;
|
|
||||||
quint16 port;
|
quint16 port;
|
||||||
|
|
||||||
while (m_socket->hasPendingDatagrams()) {
|
while (m_socket->hasPendingDatagrams()) {
|
||||||
data.resize(m_socket->pendingDatagramSize());
|
QByteArray datagram(m_socket->pendingDatagramSize(), 0);
|
||||||
m_socket->readDatagram(data.data(), data.size(), &hostAddress, &port);
|
m_socket->readDatagram(datagram.data(), datagram.size(), &hostAddress, &port);
|
||||||
|
qCDebug(dcCoap()) << "Datagram received from:" << hostAddress << ":" << datagram;
|
||||||
|
CoapPdu pdu(datagram);
|
||||||
|
processResponse(pdu, hostAddress, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
CoapPdu pdu(data);
|
|
||||||
processResponse(pdu, hostAddress, port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Coap::onReplyTimeout()
|
void Coap::onReplyTimeout()
|
||||||
|
|||||||
@ -163,6 +163,10 @@
|
|||||||
|
|
||||||
#include <QMetaEnum>
|
#include <QMetaEnum>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(dcCoap)
|
||||||
|
|
||||||
/*! Constructs a CoapPdu with the given \a parent. */
|
/*! Constructs a CoapPdu with the given \a parent. */
|
||||||
CoapPdu::CoapPdu(QObject *parent) :
|
CoapPdu::CoapPdu(QObject *parent) :
|
||||||
@ -370,7 +374,7 @@ void CoapPdu::addOption(const CoapOption::Option &option, const QByteArray &data
|
|||||||
CoapOption o;
|
CoapOption o;
|
||||||
o.setOption(option);
|
o.setOption(option);
|
||||||
o.setData(data);
|
o.setData(data);
|
||||||
m_options.insert(index + 1, o);
|
m_options.insert(qMin(m_options.length(), index + 1), o);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Returns the block of this \l{CoapPdu}. */
|
/*! Returns the block of this \l{CoapPdu}. */
|
||||||
@ -504,70 +508,97 @@ QByteArray CoapPdu::pack() const
|
|||||||
|
|
||||||
void CoapPdu::unpack(const QByteArray &data)
|
void CoapPdu::unpack(const QByteArray &data)
|
||||||
{
|
{
|
||||||
// create a CoapPDU
|
|
||||||
if (data.length() < 4) {
|
if (data.length() < 4) {
|
||||||
m_error = InvalidPduSizeError;
|
m_error = InvalidPduSizeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint8 *rawData = (quint8 *)data.data();
|
QDataStream stream(data);
|
||||||
setVersion((rawData[0] & 0xc0) >> 6);
|
quint8 flags;
|
||||||
setMessageType(static_cast<MessageType>((rawData[0] & 0x30) >> 4));
|
stream >> flags;
|
||||||
quint8 tokenLength = (rawData[0] & 0xf);
|
|
||||||
|
|
||||||
|
setVersion((flags & 0xc0) >> 6);
|
||||||
|
// qCDebug(dcCoap()) << "Version:" << m_version;
|
||||||
|
|
||||||
|
setMessageType(static_cast<MessageType>((flags & 0x30) >> 4));
|
||||||
|
// qCDebug(dcCoap()) << "Message Type:" << messageType();
|
||||||
|
|
||||||
|
quint8 tokenLength = flags & 0x0f;
|
||||||
|
// qCDebug(dcCoap()) << "Token length:" << tokenLength;
|
||||||
if (tokenLength > 8) {
|
if (tokenLength > 8) {
|
||||||
|
qCWarning(dcCoap()) << "Inavalid token length" << tokenLength;
|
||||||
m_error = InvalidTokenError;
|
m_error = InvalidTokenError;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setToken(QByteArray((const char *)rawData + 4, tokenLength));
|
quint8 reqRspCode;
|
||||||
setStatusCode(static_cast<StatusCode>(rawData[1]));
|
stream >> reqRspCode;
|
||||||
setMessageId((qint16)data.mid(2,2).toHex().toUInt(0,16));
|
setStatusCode(static_cast<StatusCode>(reqRspCode));
|
||||||
|
// qCDebug(dcCoap()) << "Req/Rsp code:" << statusCode();
|
||||||
|
|
||||||
// parse options
|
quint16 messageId;
|
||||||
int index = 4 + tokenLength;
|
stream >> messageId;
|
||||||
quint8 optionByte = rawData[index];
|
setMessageId(messageId);
|
||||||
quint16 delta = 0;
|
// qCDebug(dcCoap()) << "Message ID:" << messageId;
|
||||||
while (QByteArray::number(optionByte, 16) != "ff" && optionByte != 0) {
|
char tokenData[tokenLength];
|
||||||
quint16 optionNumber = ((optionByte & 0xf0) >> 4);
|
if (stream.readRawData(tokenData, tokenLength) != tokenLength) {
|
||||||
|
qCWarning(dcCoap()) << "Token data not complete.";
|
||||||
|
m_error = InvalidTokenError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QByteArray token(tokenData, tokenLength);
|
||||||
|
setToken(token);
|
||||||
|
// qCDebug(dcCoap()) << "Token:" << token.toHex();
|
||||||
|
|
||||||
// check option delta
|
|
||||||
if (optionNumber < 13) {
|
while (!stream.atEnd()) {
|
||||||
delta += optionNumber;
|
quint8 optionByte;
|
||||||
} else if (optionNumber == 13) {
|
stream >> optionByte;
|
||||||
// extended 8 bit option delta
|
// qCDebug(dcCoap()) << "OptionByte:" << optionByte;
|
||||||
delta += (quint8)(rawData[index + 1] + 13);
|
|
||||||
index += 1;
|
if (optionByte == 0xff) {
|
||||||
} else if (optionNumber == 14) {
|
char payloadData[65507]; // Max UDP datagram size
|
||||||
// extended 16 bit option delta
|
int payloadLength = stream.readRawData(payloadData, 65507);
|
||||||
delta += ((rawData[index + 1] << 8) | rawData[index + 2]) + 269;
|
if (payloadLength > 0) {
|
||||||
index += 2;
|
setPayload(QByteArray(payloadData, payloadLength));
|
||||||
} else if (optionNumber == 15) {
|
}
|
||||||
m_error = InvalidOptionDeltaError;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 optionDelta;
|
||||||
|
optionDelta = (optionByte & 0xf0) >> 4;
|
||||||
|
// qCDebug(dcCoap()) << "Option delta:" << optionDelta;
|
||||||
|
quint16 optionLength = (optionByte & 0x0f);
|
||||||
|
// qCDebug(dcCoap()) << "Option length:" << optionLength;
|
||||||
|
|
||||||
|
if (optionDelta == 13) {
|
||||||
|
quint8 optionDeltaExtended;
|
||||||
|
stream >> optionDeltaExtended;
|
||||||
|
optionDelta = optionDeltaExtended + 13;
|
||||||
|
// qCDebug(dcCoap()).nospace() << "Extended option delta (8 bit): " << optionDelta << " (" << optionDeltaExtended << " + 13)";
|
||||||
|
} else if (optionDelta == 14) {
|
||||||
|
quint16 optionDeltaExtended;
|
||||||
|
stream >> optionDeltaExtended;
|
||||||
|
optionDelta = optionDeltaExtended + 269;
|
||||||
|
// qCDebug(dcCoap()).nospace() << "Extended option delta (16 bit): " << optionDelta << " (" << optionDeltaExtended << " + 269)";
|
||||||
}
|
}
|
||||||
|
|
||||||
// check option length
|
|
||||||
quint16 optionLength = (optionByte & 0xf);
|
|
||||||
if (optionLength == 13) {
|
if (optionLength == 13) {
|
||||||
// extended 8 bit option length
|
quint8 optionLengthExtended;
|
||||||
optionLength = (quint8)(rawData[index + 1] - 13);
|
stream >> optionLengthExtended;
|
||||||
index += 1;
|
optionLength = optionLengthExtended + 13;
|
||||||
|
// qCDebug(dcCoap()).nospace() << "Extended option length (8 bit): " << optionLength << " (" << optionLengthExtended << " + 13)";
|
||||||
} else if (optionLength == 14) {
|
} else if (optionLength == 14) {
|
||||||
// extended 16 bit option delta
|
quint16 optionLengthExtended;
|
||||||
optionLength = ((rawData[index + 1] << 8) | rawData[index + 2]) - 269;
|
stream >> optionLengthExtended;
|
||||||
index += 2;
|
optionLength = optionLengthExtended + 269;
|
||||||
} else if (optionLength == 15) {
|
// qCDebug(dcCoap()).nospace() << "Extended option kength (16 bit): " << optionDelta << " (" << optionLengthExtended << " + 269)";
|
||||||
m_error = InvalidOptionLengthError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray optionData = QByteArray((const char *)rawData + index + 1, optionLength);
|
char optionData[optionLength];
|
||||||
addOption(static_cast<CoapOption::Option>(delta), optionData);
|
stream.readRawData(optionData, optionLength);
|
||||||
|
// qCDebug(dcCoap()) << "Option data:" << QByteArray(optionData, optionLength);
|
||||||
|
|
||||||
index += optionLength + 1;
|
addOption(static_cast<CoapOption::Option>(optionDelta), QByteArray(optionData, optionLength));
|
||||||
optionByte = rawData[index];
|
|
||||||
|
|
||||||
if (QByteArray::number(optionByte, 16) == "ff") {
|
|
||||||
setPayload(data.right(data.length() - index - 1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -68,6 +68,7 @@ public:
|
|||||||
Acknowledgement = 0x02,
|
Acknowledgement = 0x02,
|
||||||
Reset = 0x03
|
Reset = 0x03
|
||||||
};
|
};
|
||||||
|
Q_ENUM(MessageType)
|
||||||
|
|
||||||
// Methods: https://tools.ietf.org/html/rfc7252#section-5.8
|
// Methods: https://tools.ietf.org/html/rfc7252#section-5.8
|
||||||
// Respond codes: https://tools.ietf.org/html/rfc7252#section-12.1.2
|
// Respond codes: https://tools.ietf.org/html/rfc7252#section-12.1.2
|
||||||
@ -101,6 +102,7 @@ public:
|
|||||||
GatewayTimeout = 0xa4, // 5.04
|
GatewayTimeout = 0xa4, // 5.04
|
||||||
ProxyingNotSupported = 0xa5 // 5.05
|
ProxyingNotSupported = 0xa5 // 5.05
|
||||||
};
|
};
|
||||||
|
Q_ENUM(StatusCode)
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc7252#section-12.3
|
// https://tools.ietf.org/html/rfc7252#section-12.3
|
||||||
enum ContentType {
|
enum ContentType {
|
||||||
@ -111,6 +113,7 @@ public:
|
|||||||
ApplicationExi = 47,
|
ApplicationExi = 47,
|
||||||
ApplicationJson = 50
|
ApplicationJson = 50
|
||||||
};
|
};
|
||||||
|
Q_ENUM(ContentType)
|
||||||
|
|
||||||
enum Error {
|
enum Error {
|
||||||
NoError,
|
NoError,
|
||||||
@ -120,6 +123,7 @@ public:
|
|||||||
InvalidOptionLengthError,
|
InvalidOptionLengthError,
|
||||||
UnknownOptionError
|
UnknownOptionError
|
||||||
};
|
};
|
||||||
|
Q_ENUM(Error)
|
||||||
|
|
||||||
CoapPdu(QObject *parent = 0);
|
CoapPdu(QObject *parent = 0);
|
||||||
CoapPdu(const QByteArray &data, QObject *parent = 0);
|
CoapPdu(const QByteArray &data, QObject *parent = 0);
|
||||||
|
|||||||
@ -132,8 +132,8 @@ private:
|
|||||||
int m_messageId;
|
int m_messageId;
|
||||||
QByteArray m_messageToken;
|
QByteArray m_messageToken;
|
||||||
|
|
||||||
bool m_observation;
|
bool m_observation = false;
|
||||||
bool m_observationEnable;
|
bool m_observationEnable = false;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void timeout();
|
void timeout();
|
||||||
|
|||||||
Reference in New Issue
Block a user