1// Copyright (C) 2017 Witekio.
2// Copyright (C) 2018 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4// Qt-Security score:critical reason:network-protocol
5
6#include "qcoapinternalreply_p.h"
7#include <QtCore/qmath.h>
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 \internal
13
14 \class QCoapInternalReply
15 \brief The QCoapInternalReply class contains data related to
16 a received message.
17
18 It is a subclass of QCoapInternalMessage.
19
20 \reentrant
21
22 \sa QCoapInternalMessage, QCoapInternalRequest
23*/
24
25/*!
26 \internal
27 Constructs a new QCoapInternalReply with \a parent as the parent object.
28*/
29QCoapInternalReply::QCoapInternalReply(QObject *parent) :
30 QCoapInternalMessage(*new QCoapInternalReplyPrivate, parent)
31{
32}
33
34/*!
35 \internal
36 Creates a QCoapInternalReply from the CoAP \a reply frame.
37
38 For more details, refer to section
39 \l{https://tools.ietf.org/html/rfc7252#section-3}{'Message format' of RFC 7252}.
40*/
41//! 0 1 2 3
42//! 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
43//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44//! |Ver| T | TKL | Code | Message ID |
45//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46//! | Token (if any, TKL bytes) ...
47//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48//! | Options (if any) ...
49//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50//! |1 1 1 1 1 1 1 1| Payload (if any) ...
51//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52QCoapInternalReply *QCoapInternalReply::createFromFrame(const QByteArray &reply, QObject *parent)
53{
54 QCoapInternalReply *internalReply = new QCoapInternalReply(parent);
55 QCoapInternalReplyPrivate *d = internalReply->d_func();
56
57 const quint8 *pduData = reinterpret_cast<const quint8 *>(reply.data());
58
59 // Parse Header and Token
60 d->message.setVersion((pduData[0] >> 6) & 0x03);
61 d->message.setType(QCoapMessage::Type((pduData[0] >> 4) & 0x03));
62 quint8 tokenLength = (pduData[0]) & 0x0F;
63 d->responseCode = static_cast<QtCoap::ResponseCode>(pduData[1]);
64 d->message.setMessageId(static_cast<quint16>((static_cast<quint16>(pduData[2]) << 8)
65 | static_cast<quint16>(pduData[3])));
66 d->message.setToken(reply.mid(index: 4, len: tokenLength));
67
68 // Parse Options
69 int i = 4 + tokenLength;
70 quint16 lastOptionNumber = 0;
71 while (i != reply.size() && pduData[i] != 0xFF) {
72 quint16 optionDelta = ((pduData[i] >> 4) & 0x0F);
73 quint16 optionLength = (pduData[i] & 0x0F);
74
75 // Delta value > 12 : special values
76 if (optionDelta == 13) {
77 ++i;
78 optionDelta = pduData[i] + 13;
79 } else if (optionDelta == 14) {
80 ++i;
81 optionDelta = pduData[i] + 269;
82 }
83
84 // Delta length > 12 : special values
85 if (optionLength == 13) {
86 ++i;
87 optionLength = pduData[i] + 13;
88 } else if (optionLength == 14) {
89 ++i;
90 optionLength = pduData[i] + 269;
91 }
92
93 quint16 optionNumber = lastOptionNumber + optionDelta;
94 internalReply->addOption(name: QCoapOption::OptionName(optionNumber),
95 value: reply.mid(index: i + 1, len: optionLength));
96 lastOptionNumber = optionNumber;
97 i += 1 + optionLength;
98 }
99
100 // Parse Payload
101 if (i < reply.size() && pduData[i] == 0xFF) {
102 // -1 because of 0xFF at the beginning
103 QByteArray currentPayload = reply.mid(index: i + 1);
104 d->message.setPayload(d->message.payload().append(a: currentPayload));
105 }
106
107 return internalReply;
108}
109
110/*!
111 \internal
112 Appends the given \a data byte array to the current payload.
113*/
114void QCoapInternalReply::appendData(const QByteArray &data)
115{
116 Q_D(QCoapInternalReply);
117 d->message.setPayload(d->message.payload().append(a: data));
118}
119
120/*!
121 \internal
122 Adds the given CoAP \a option and sets block parameters if needed.
123*/
124void QCoapInternalReply::addOption(const QCoapOption &option)
125{
126 if (option.name() == QCoapOption::Block2)
127 setFromDescriptiveBlockOption(option);
128
129 QCoapInternalMessage::addOption(option);
130}
131
132/*!
133 \internal
134 Sets the sender address.
135*/
136void QCoapInternalReply::setSenderAddress(const QHostAddress &address)
137{
138 Q_D(QCoapInternalReply);
139 d->senderAddress = address;
140}
141
142/*!
143 \internal
144 Returns the number of the next block, if there is another block to come,
145 otherwise -1.
146 For more details, refer to the
147 \l{https://tools.ietf.org/html/rfc7959#section-2.2}{RFC 7959}.
148*/
149int QCoapInternalReply::nextBlockToSend() const
150{
151 Q_D(const QCoapInternalReply);
152
153 QCoapOption option = d->message.option(name: QCoapOption::Block1);
154 if (!option.isValid())
155 return -1;
156
157 const auto value = option.opaqueValue();
158 const quint8 *optionData = reinterpret_cast<const quint8 *>(value.data());
159 const quint8 lastByte = optionData[option.length() - 1];
160
161 // M field
162 bool hasNextBlock = ((lastByte & 0x8) == 0x8);
163 if (!hasNextBlock)
164 return -1;
165
166 // NUM field
167 quint32 blockNumber = 0;
168 for (int i = 0; i < option.length() - 1; ++i)
169 blockNumber = (blockNumber << 8) | optionData[i];
170 blockNumber = (blockNumber << 4) | (lastByte >> 4);
171 return static_cast<int>(blockNumber) + 1;
172}
173
174/*!
175 \internal
176 Returns \c true if the client has one or more blocks to send.
177*/
178bool QCoapInternalReply::hasMoreBlocksToSend() const
179{
180 return nextBlockToSend() >= 0;
181}
182
183/*!
184 \internal
185 Returns the response code of the reply.
186*/
187QtCoap::ResponseCode QCoapInternalReply::responseCode() const
188{
189 Q_D(const QCoapInternalReply);
190 return d->responseCode;
191}
192
193/*!
194 \internal
195 Returns the host address from which the reply was received.
196*/
197QHostAddress QCoapInternalReply::senderAddress() const
198{
199 Q_D(const QCoapInternalReply);
200 return d->senderAddress;
201}
202
203QT_END_NAMESPACE
204

source code of qtcoap/src/coap/qcoapinternalreply.cpp