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 "qcoapprotocol_p.h"
7#include "qcoapinternalrequest_p.h"
8#include "qcoapinternalreply_p.h"
9#include "qcoaprequest_p.h"
10#include "qcoapconnection_p.h"
11#include "qcoapnamespace_p.h"
12
13#include <QtCore/qrandom.h>
14#include <QtCore/qthread.h>
15#include <QtCore/qloggingcategory.h>
16#include <QtNetwork/qnetworkdatagram.h>
17
18QT_BEGIN_NAMESPACE
19
20Q_STATIC_LOGGING_CATEGORY(lcCoapProtocol, "qt.coap.protocol")
21
22/*!
23 \internal
24
25 \class QCoapProtocol
26 \inmodule QtCoap
27
28 \brief The QCoapProtocol class handles the logical part of the CoAP
29 protocol.
30
31 \reentrant
32
33 The QCoapProtocol is used by the QCoapClient class to handle the logical
34 part of the protocol. It can encode requests and decode replies. It also
35 handles what to do when a message is received, along with retransmission of
36 lost messages.
37
38 \sa QCoapClient
39*/
40
41/*!
42 \internal
43
44 \fn void QCoapProtocol::finished(QCoapReply *reply)
45
46 This signal is emitted along with the \l QCoapReply::finished() signal
47 whenever a CoAP reply is received, after either a success or an error.
48 The \a reply parameter will contain a pointer to the reply that has just
49 been received.
50
51 \sa error(), QCoapReply::finished(), QCoapReply::error()
52*/
53
54/*!
55 \internal
56
57 \fn void QCoapProtocol::responseToMulticastReceived(QCoapReply *reply,
58 const QCoapMessage& message,
59 const QHostAddress &sender)
60
61 This signal is emitted when a unicast response to a multicast request
62 arrives. The \a reply parameter contains a pointer to the reply that has just
63 been received, \a message contains the payload and the message details,
64 and \a sender contains the sender address.
65
66 \sa error(), QCoapReply::finished(), QCoapReply::error()
67*/
68
69/*!
70 \internal
71
72 \fn void QCoapProtocol::error(QCoapReply *reply, QtCoap::Error error)
73
74 This signal is emitted whenever an error occurs. The \a reply parameter
75 can be \nullptr if the error is not related to a specific QCoapReply. The
76 \a error parameter contains the error code.
77
78 \sa finished(), QCoapReply::error(), QCoapReply::finished()
79*/
80
81/*!
82 \internal
83
84 Constructs a new QCoapProtocol and sets \a parent as the parent object.
85*/
86QCoapProtocol::QCoapProtocol(QObject *parent) :
87 QObject(*new QCoapProtocolPrivate, parent)
88{
89 qRegisterMetaType<QCoapInternalRequest *>();
90 qRegisterMetaType<QHostAddress>();
91}
92
93QCoapProtocol::~QCoapProtocol()
94{
95 Q_D(QCoapProtocol);
96
97 // Clear table to avoid double deletion from QObject parenting and QSharedPointer.
98 d->exchangeMap.clear();
99}
100
101/*!
102 \internal
103
104 Creates and sets up a new QCoapInternalRequest related to the request
105 associated to the \a reply. The request will then be sent to the server
106 using the given \a connection.
107*/
108void QCoapProtocol::sendRequest(QPointer<QCoapReply> reply, QCoapConnection *connection)
109{
110 Q_D(QCoapProtocol);
111 Q_ASSERT(QThread::currentThread() == thread());
112
113 if (reply.isNull() || reply->request().method() == QtCoap::Method::Invalid
114 || !QCoapRequestPrivate::isUrlValid(url: reply->request().url()))
115 return;
116
117 connect(sender: reply.data(), signal: &QCoapReply::aborted, context: this, slot: [this](const QCoapToken &token) {
118 Q_D(QCoapProtocol);
119 d->onRequestAborted(token);
120 });
121
122 auto internalRequest = QSharedPointer<QCoapInternalRequest>::create(arguments: reply->request(), arguments: this);
123 internalRequest->setMaxTransmissionWait(maximumTransmitWait());
124 connect(sender: reply.data(), signal: &QCoapReply::finished, context: this, slot: &QCoapProtocol::finished);
125
126 if (internalRequest->isMulticast()) {
127 connect(sender: internalRequest.data(), signal: &QCoapInternalRequest::multicastRequestExpired, context: this,
128 slot: [this](QCoapInternalRequest *request) {
129 Q_D(QCoapProtocol);
130 d->onMulticastRequestExpired(request);
131 });
132 // The timeout interval is chosen based on
133 // https://tools.ietf.org/html/rfc7390#section-2.5
134 internalRequest->setMulticastTimeout(nonConfirmLifetime()
135 + maximumLatency()
136 + maximumServerResponseDelay());
137 }
138
139 // Set a unique Message Id and Token
140 QCoapMessage *requestMessage = internalRequest->message();
141 internalRequest->setMessageId(d->generateUniqueMessageId());
142 if (internalRequest->token().isEmpty())
143 internalRequest->setToken(d->generateUniqueToken());
144 internalRequest->setConnection(connection);
145
146 d->registerExchange(token: requestMessage->token(), reply, request: internalRequest);
147 QMetaObject::invokeMethod(obj: reply, member: "_q_setRunning", c: Qt::QueuedConnection,
148 Q_ARG(QCoapToken, requestMessage->token()),
149 Q_ARG(QCoapMessageId, requestMessage->messageId()));
150
151 // Set block size for blockwise request/replies, if specified
152 if (d->blockSize > 0) {
153 internalRequest->setToRequestBlock(blockNumber: 0, blockSize: d->blockSize);
154 if (requestMessage->payload().size() > d->blockSize)
155 internalRequest->setToSendBlock(blockNumber: 0, blockSize: d->blockSize);
156 }
157
158 if (requestMessage->type() == QCoapMessage::Type::Confirmable) {
159 const auto minTimeout = minimumTimeout();
160 const auto maxTimeout = maximumTimeout();
161 Q_ASSERT(minTimeout <= maxTimeout);
162
163 internalRequest->setTimeout(minTimeout == maxTimeout
164 ? minTimeout
165 : QtCoap::randomGenerator().bounded(lowest: minTimeout, highest: maxTimeout));
166 } else {
167 internalRequest->setTimeout(maximumTimeout());
168 }
169
170 connect(sender: internalRequest.data(), signal: &QCoapInternalRequest::timeout, context: this,
171 slot: [this](QCoapInternalRequest *request) {
172 Q_D(QCoapProtocol);
173 d->onRequestTimeout(request);
174 });
175 connect(sender: internalRequest.data(), signal: &QCoapInternalRequest::maxTransmissionSpanReached, context: this,
176 slot: [this](QCoapInternalRequest *request) {
177 Q_D(QCoapProtocol);
178 d->onRequestMaxTransmissionSpanReached(request);
179 });
180 d->sendRequest(request: internalRequest.data());
181}
182
183/*!
184 \internal
185
186 Encodes and sends the given \a request to the server. If \a host is not empty,
187 sends the request to \a host, instead of using the host address from the request.
188 The \a host parameter is relevant for multicast blockwise transfers.
189*/
190void QCoapProtocolPrivate::sendRequest(QCoapInternalRequest *request, const QString& host) const
191{
192 Q_Q(const QCoapProtocol);
193 Q_ASSERT(QThread::currentThread() == q->thread());
194
195 if (!request || !request->connection()) {
196 qCWarning(lcCoapProtocol, "Request null or not bound to any connection: aborted.");
197 return;
198 }
199
200 if (request->isMulticast())
201 request->startMulticastTransmission();
202 else
203 request->restartTransmission();
204
205 QByteArray requestFrame = request->toQByteArray();
206 QUrl uri = request->targetUri();
207 const auto& hostAddress = host.isEmpty() ? uri.host() : host;
208 request->connection()->d_func()->sendRequest(request: requestFrame, host: hostAddress,
209 port: static_cast<quint16>(uri.port()));
210}
211
212/*!
213 \internal
214
215 This slot is used to send again the given \a request after a timeout or
216 aborts the request and transfers a timeout error to the reply.
217*/
218void QCoapProtocolPrivate::onRequestTimeout(QCoapInternalRequest *request)
219{
220 Q_Q(const QCoapProtocol);
221 Q_ASSERT(QThread::currentThread() == q->thread());
222
223 if (!isRequestRegistered(request))
224 return;
225
226 if (request->message()->type() == QCoapMessage::Type::Confirmable
227 && request->retransmissionCounter() < maximumRetransmitCount) {
228 sendRequest(request);
229 } else {
230 onRequestError(request, error: QtCoap::Error::TimeOut);
231 }
232}
233
234/*!
235 \internal
236
237 This slot is called when the maximum span for this transmission has been
238 reached, and triggers a timeout error if the request is still running.
239*/
240void QCoapProtocolPrivate::onRequestMaxTransmissionSpanReached(QCoapInternalRequest *request)
241{
242 Q_Q(const QCoapProtocol);
243 Q_ASSERT(QThread::currentThread() == q->thread());
244
245 if (isRequestRegistered(request))
246 onRequestError(request, error: QtCoap::Error::TimeOut);
247}
248
249/*!
250 \internal
251
252 This slot is called when the multicast request expires, meaning that no
253 more responses are expected for the multicast \a request. As a result of this
254 call, the request token is \e {freed up} and the \l finished() signal is emitted.
255*/
256void QCoapProtocolPrivate::onMulticastRequestExpired(QCoapInternalRequest *request)
257{
258 Q_ASSERT(request->isMulticast());
259
260 request->stopTransmission();
261 QPointer<QCoapReply> userReply = userReplyForToken(token: request->token());
262 if (userReply) {
263 QMetaObject::invokeMethod(obj: userReply, member: "_q_setFinished", c: Qt::QueuedConnection,
264 Q_ARG(QtCoap::Error, QtCoap::Error::Ok));
265 } else {
266 qCWarning(lcCoapProtocol).nospace() << "Reply for token '" << request->token()
267 << "' is not registered, reply is null.";
268 }
269 forgetExchange(request);
270}
271
272/*!
273 \internal
274
275 Method triggered when a request fails.
276*/
277void QCoapProtocolPrivate::onRequestError(QCoapInternalRequest *request, QCoapInternalReply *reply)
278{
279 QtCoap::Error error = QtCoap::errorForResponseCode(code: reply->responseCode());
280 onRequestError(request, error, reply);
281}
282
283/*!
284 \internal
285
286 Method triggered when a request fails.
287*/
288void QCoapProtocolPrivate::onRequestError(QCoapInternalRequest *request, QtCoap::Error error,
289 QCoapInternalReply *reply)
290{
291 Q_Q(QCoapProtocol);
292 Q_ASSERT(request);
293
294 auto userReply = userReplyForToken(token: request->token());
295
296 if (!userReply.isNull()) {
297 // Set error from content, or error enum
298 if (reply) {
299 QMetaObject::invokeMethod(obj: userReply.data(), member: "_q_setContent", c: Qt::QueuedConnection,
300 Q_ARG(QHostAddress, reply->senderAddress()),
301 Q_ARG(QCoapMessage, *reply->message()),
302 Q_ARG(QtCoap::ResponseCode, reply->responseCode()));
303 } else {
304 QMetaObject::invokeMethod(obj: userReply.data(), member: "_q_setError", c: Qt::QueuedConnection,
305 Q_ARG(QtCoap::Error, error));
306 }
307
308 QMetaObject::invokeMethod(obj: userReply.data(), member: "_q_setFinished", c: Qt::QueuedConnection,
309 Q_ARG(QtCoap::Error, QtCoap::Error::Ok));
310 }
311
312 forgetExchange(request);
313 emit q->error(reply: userReply.data(), error);
314}
315
316/*!
317 \internal
318
319 Decode and process the given \a data received from the \a sender.
320*/
321void QCoapProtocolPrivate::onFrameReceived(const QByteArray &data, const QHostAddress &sender)
322{
323 Q_Q(const QCoapProtocol);
324 Q_ASSERT(QThread::currentThread() == q->thread());
325
326 QSharedPointer<QCoapInternalReply> reply(decode(data, sender));
327 const QCoapMessage *messageReceived = reply->message();
328
329 QCoapInternalRequest *request = nullptr;
330 if (!messageReceived->token().isEmpty())
331 request = requestForToken(token: messageReceived->token());
332
333 if (!request) {
334 request = findRequestByMessageId(messageId: messageReceived->messageId());
335
336 // No matching request found, drop the frame.
337 if (!request)
338 return;
339 }
340
341 QHostAddress originalTarget(request->targetUri().host());
342 if (!originalTarget.isMulticast() && !originalTarget.isEqual(address: sender)) {
343 qCDebug(lcCoapProtocol).nospace() << "QtCoap: Answer received from incorrect host ("
344 << sender << " instead of "
345 << originalTarget << ")";
346 return;
347 }
348
349 if (!request->isMulticast())
350 request->stopTransmission();
351 addReply(token: request->token(), reply);
352
353 if (QtCoap::isError(code: reply->responseCode())) {
354 onRequestError(request, reply: reply.data());
355 return;
356 }
357
358 // Reply when the server asks for an ACK
359 if (request->isObserveCancelled()) {
360 // Remove option to ensure that it will stop
361 request->removeOption(name: QCoapOption::Observe);
362 sendReset(request);
363 } else if (messageReceived->type() == QCoapMessage::Type::Confirmable) {
364 sendAcknowledgment(request);
365 }
366
367 // Send next block, ask for next block, or process the final reply
368 if (reply->hasMoreBlocksToSend() && reply->nextBlockToSend() >= 0) {
369 request->setToSendBlock(blockNumber: static_cast<uint>(reply->nextBlockToSend()), blockSize);
370 request->setMessageId(generateUniqueMessageId());
371 sendRequest(request);
372 } else if (reply->hasMoreBlocksToReceive()) {
373 request->setToRequestBlock(blockNumber: reply->currentBlockNumber() + 1, blockSize: reply->blockSize());
374 request->setMessageId(generateUniqueMessageId());
375 // In case of multicast blockwise transfers, according to
376 // https://tools.ietf.org/html/rfc7959#section-2.8, further blocks should be retrieved
377 // via unicast requests. So instead of using the multicast request address, we need
378 // to use the sender address for getting the next blocks.
379 sendRequest(request, host: sender.toString());
380 } else {
381 onLastMessageReceived(request, sender);
382 }
383}
384
385/*!
386 \internal
387
388 Returns the internal request for the given \a token.
389*/
390QCoapInternalRequest *QCoapProtocolPrivate::requestForToken(const QCoapToken &token) const
391{
392 auto it = exchangeMap.find(key: token);
393 if (it != exchangeMap.constEnd())
394 return it->request.data();
395
396 return nullptr;
397}
398
399/*!
400 \internal
401
402 Returns the QCoapReply instance of the given \a token.
403*/
404QPointer<QCoapReply> QCoapProtocolPrivate::userReplyForToken(const QCoapToken &token) const
405{
406 auto it = exchangeMap.find(key: token);
407 if (it != exchangeMap.constEnd())
408 return it->userReply;
409
410 return nullptr;
411}
412
413/*!
414 \internal
415
416 Returns the replies for the exchange identified by \a token.
417*/
418QList<QSharedPointer<QCoapInternalReply>>
419QCoapProtocolPrivate::repliesForToken(const QCoapToken &token) const
420{
421 auto it = exchangeMap.find(key: token);
422 if (it != exchangeMap.constEnd())
423 return it->replies;
424
425 return {};
426}
427
428/*!
429 \internal
430
431 Returns the last reply for the exchange identified by \a token.
432*/
433QCoapInternalReply *QCoapProtocolPrivate::lastReplyForToken(const QCoapToken &token) const
434{
435 auto it = exchangeMap.find(key: token);
436 if (it != exchangeMap.constEnd())
437 return it->replies.last().data();
438
439 return nullptr;
440}
441
442/*!
443 \internal
444
445 Finds an internal request matching the given \a reply.
446*/
447QCoapInternalRequest *QCoapProtocolPrivate::findRequestByUserReply(const QCoapReply *reply) const
448{
449 for (auto it = exchangeMap.constBegin(); it != exchangeMap.constEnd(); ++it) {
450 if (it->userReply == reply)
451 return it->request.data();
452 }
453
454 return nullptr;
455}
456
457/*!
458 \internal
459
460 Finds an internal request containing the message id \a messageId.
461*/
462QCoapInternalRequest *QCoapProtocolPrivate::findRequestByMessageId(quint16 messageId) const
463{
464 for (auto it = exchangeMap.constBegin(); it != exchangeMap.constEnd(); ++it) {
465 if (it->request->message()->messageId() == messageId)
466 return it->request.data();
467 }
468
469 return nullptr;
470}
471
472/*!
473 \internal
474
475 Handles what to do when we received the last block of a reply.
476
477 Merges all blocks, removes the request from the map, updates the
478 associated QCoapReply and emits the
479 \l{QCoapProtocol::finished(QCoapReply*)}{finished(QCoapReply*)} signal.
480*/
481void QCoapProtocolPrivate::onLastMessageReceived(QCoapInternalRequest *request,
482 const QHostAddress &sender)
483{
484 Q_ASSERT(request);
485 if (!request || !isRequestRegistered(request))
486 return;
487
488 auto replies = repliesForToken(token: request->token());
489 Q_ASSERT(!replies.isEmpty());
490
491 //! TODO: Change QPointer<QCoapReply> into something independent from
492 //! User. QSharedPointer(s)?
493 QPointer<QCoapReply> userReply = userReplyForToken(token: request->token());
494 if (userReply.isNull() || replies.isEmpty()
495 || (request->isObserve() && request->isObserveCancelled())) {
496 forgetExchange(request);
497 return;
498 }
499
500 auto lastReply = replies.last();
501 // Ignore empty ACK messages
502 if (lastReply->message()->type() == QCoapMessage::Type::Acknowledgment
503 && lastReply->responseCode() == QtCoap::ResponseCode::EmptyMessage) {
504 exchangeMap[request->token()].replies.takeLast();
505 return;
506 }
507
508 // Merge payloads for blockwise transfers
509 if (replies.size() > 1) {
510
511 // In multicast case, multiple hosts will reply to the same multicast request.
512 // We are interested only in replies coming from the sender.
513 if (request->isMulticast()) {
514 replies.erase(abegin: std::remove_if(first: replies.begin(), last: replies.end(),
515 pred: [sender](QSharedPointer<QCoapInternalReply> reply) {
516 return reply->senderAddress() != sender;
517 }), aend: replies.end());
518 }
519
520 std::stable_sort(first: std::begin(cont&: replies), last: std::end(cont&: replies),
521 comp: [](QSharedPointer<QCoapInternalReply> a, QSharedPointer<QCoapInternalReply> b) -> bool {
522 return (a->currentBlockNumber() < b->currentBlockNumber());
523 });
524
525 QByteArray finalPayload;
526 int lastBlockNumber = -1;
527 for (auto reply : std::as_const(t&: replies)) {
528 int currentBlock = static_cast<int>(reply->currentBlockNumber());
529 QByteArray replyPayload = reply->message()->payload();
530 if (replyPayload.isEmpty() || currentBlock <= lastBlockNumber)
531 continue;
532
533 finalPayload.append(a: replyPayload);
534 lastBlockNumber = currentBlock;
535 }
536
537 lastReply->message()->setPayload(finalPayload);
538 }
539
540 // Forward the answer
541 QMetaObject::invokeMethod(obj: userReply, member: "_q_setContent", c: Qt::QueuedConnection,
542 Q_ARG(QHostAddress, lastReply->senderAddress()),
543 Q_ARG(QCoapMessage, *lastReply->message()),
544 Q_ARG(QtCoap::ResponseCode, lastReply->responseCode()));
545
546 if (request->isObserve()) {
547 QMetaObject::invokeMethod(obj: userReply, member: "_q_setNotified", c: Qt::QueuedConnection);
548 forgetExchangeReplies(token: request->token());
549 } else if (request->isMulticast()) {
550 Q_Q(QCoapProtocol);
551 emit q->responseToMulticastReceived(reply: userReply, message: *lastReply->message(), sender);
552 } else {
553 QMetaObject::invokeMethod(obj: userReply, member: "_q_setFinished", c: Qt::QueuedConnection,
554 Q_ARG(QtCoap::Error, QtCoap::Error::Ok));
555 forgetExchange(request);
556 }
557}
558
559/*!
560 \internal
561
562 Sends an internal request acknowledging the given \a request, reusing its
563 URI and connection.
564*/
565void QCoapProtocolPrivate::sendAcknowledgment(QCoapInternalRequest *request) const
566{
567 Q_Q(const QCoapProtocol);
568 Q_ASSERT(QThread::currentThread() == q->thread());
569
570 QCoapInternalRequest ackRequest;
571 ackRequest.setTargetUri(request->targetUri());
572
573 auto internalReply = lastReplyForToken(token: request->token());
574 ackRequest.initEmptyMessage(messageId: internalReply->message()->messageId(),
575 type: QCoapMessage::Type::Acknowledgment);
576 ackRequest.setConnection(request->connection());
577 sendRequest(request: &ackRequest);
578}
579
580/*!
581 \internal
582
583 Sends a Reset message (RST), reusing the details of the given
584 \a request. A Reset message indicates that a specific message has been
585 received, but cannot be properly processed.
586*/
587void QCoapProtocolPrivate::sendReset(QCoapInternalRequest *request) const
588{
589 Q_Q(const QCoapProtocol);
590 Q_ASSERT(QThread::currentThread() == q->thread());
591
592 QCoapInternalRequest resetRequest;
593 resetRequest.setTargetUri(request->targetUri());
594
595 auto lastReply = lastReplyForToken(token: request->token());
596 resetRequest.initEmptyMessage(messageId: lastReply->message()->messageId(), type: QCoapMessage::Type::Reset);
597 resetRequest.setConnection(request->connection());
598 sendRequest(request: &resetRequest);
599}
600
601/*!
602 \internal
603
604 Cancels resource observation. The QCoapReply::notified() signal will not
605 be emitted after cancellation.
606
607 A Reset (RST) message will be sent at the reception of the next message.
608*/
609void QCoapProtocol::cancelObserve(QPointer<QCoapReply> reply) const
610{
611 Q_D(const QCoapProtocol);
612
613 if (reply.isNull())
614 return;
615
616 QCoapInternalRequest *request = d->requestForToken(token: reply->request().token());
617 if (request) {
618 // Stop here if already cancelled
619 if (!request->isObserve() || request->isObserveCancelled())
620 return;
621
622 request->setObserveCancelled();
623 }
624
625 // Set as cancelled even if request is not tracked anymore
626 QMetaObject::invokeMethod(obj: reply, member: "_q_setObserveCancelled", c: Qt::QueuedConnection);
627}
628
629/*!
630 \internal
631
632 Cancels resource observation for the given \a url. The QCoapReply::notified()
633 signal will not be emitted after cancellation.
634
635 A Reset (RST) message will be sent at the reception of the next message.
636*/
637void QCoapProtocol::cancelObserve(const QUrl &url) const
638{
639 Q_D(const QCoapProtocol);
640
641 for (const auto &exchange : d->exchangeMap) {
642 Q_ASSERT(exchange.userReply);
643 if (exchange.userReply->url() == url)
644 cancelObserve(reply: exchange.userReply);
645 }
646}
647
648/*!
649 \internal
650
651 Returns a currently unused message Id.
652*/
653quint16 QCoapProtocolPrivate::generateUniqueMessageId() const
654{
655 // TODO: Optimize message id generation for large sets
656 // TODO: Store used message id for the period specified by CoAP spec
657 quint16 id = 0;
658 while (isMessageIdRegistered(id))
659 id = static_cast<quint16>(QtCoap::randomGenerator().bounded(highest: 0x10000));
660
661 return id;
662}
663
664/*!
665 \internal
666
667 Returns a currently unused token.
668*/
669QCoapToken QCoapProtocolPrivate::generateUniqueToken() const
670{
671 // TODO: Optimize token generation for large sets
672 // TODO: Store used token for the period specified by CoAP spec
673 QCoapToken token;
674 while (isTokenRegistered(token)) {
675 quint8 length = static_cast<quint8>(QtCoap::randomGenerator().bounded(lowest: minimumTokenSize, highest: 9));
676 token.resize(size: length);
677 quint8 *tokenData = reinterpret_cast<quint8 *>(token.data());
678 for (int i = 0; i < token.size(); ++i)
679 tokenData[i] = static_cast<quint8>(QtCoap::randomGenerator().bounded(highest: 256));
680 }
681
682 return token;
683}
684
685/*!
686 \internal
687
688 Returns a new unmanaged QCoapInternalReply based on \a data and \a sender.
689*/
690QCoapInternalReply *QCoapProtocolPrivate::decode(const QByteArray &data, const QHostAddress &sender)
691{
692 Q_Q(QCoapProtocol);
693 QCoapInternalReply *reply = QCoapInternalReply::createFromFrame(frame: data, parent: q);
694 reply->setSenderAddress(sender);
695
696 return reply;
697}
698
699/*!
700 \internal
701
702 Aborts the request corresponding to the given \a reply. It is triggered
703 by the destruction of the QCoapReply object or a call to
704 QCoapReply::abortRequest().
705*/
706void QCoapProtocolPrivate::onRequestAborted(const QCoapToken &token)
707{
708 QCoapInternalRequest *request = requestForToken(token);
709 if (!request)
710 return;
711
712 request->stopTransmission();
713 forgetExchange(request);
714}
715
716/*!
717 \internal
718
719 Triggered in case of a connection error.
720*/
721void QCoapProtocolPrivate::onConnectionError(QAbstractSocket::SocketError socketError)
722{
723 Q_Q(QCoapProtocol);
724
725 QtCoap::Error coapError;
726 switch (socketError) {
727 case QAbstractSocket::HostNotFoundError :
728 coapError = QtCoap::Error::HostNotFound;
729 break;
730 case QAbstractSocket::AddressInUseError :
731 coapError = QtCoap::Error::AddressInUse;
732 break;
733 default:
734 coapError = QtCoap::Error::Unknown;
735 break;
736 }
737
738 emit q->error(reply: nullptr, error: coapError);
739}
740
741/*!
742 \internal
743
744 Registers a new CoAP exchange using \a token.
745*/
746void QCoapProtocolPrivate::registerExchange(const QCoapToken &token, QCoapReply *reply,
747 QSharedPointer<QCoapInternalRequest> request)
748{
749 CoapExchangeData data = { .userReply: reply, .request: request,
750 .replies: QList<QSharedPointer<QCoapInternalReply> >()
751 };
752
753 exchangeMap.insert(key: token, value: data);
754}
755
756/*!
757 \internal
758
759 Adds \a reply to the list of replies of the exchange identified by
760 \a token.
761 Returns \c true if the reply was successfully added. This method will fail
762 and return \c false if no exchange is associated with the \a token
763 provided.
764*/
765bool QCoapProtocolPrivate::addReply(const QCoapToken &token,
766 QSharedPointer<QCoapInternalReply> reply)
767{
768 if (!isTokenRegistered(token) || !reply) {
769 qCWarning(lcCoapProtocol).nospace() << "Reply token '" << token
770 << "' not registered, or reply is null.";
771 return false;
772 }
773
774 exchangeMap[token].replies.push_back(t: reply);
775 return true;
776}
777
778/*!
779 \internal
780
781 Remove the exchange identified by its \a token. This is
782 typically done when finished or aborted.
783 It will delete the QCoapInternalRequest and QCoapInternalReplies
784 associated with the exchange.
785
786 Returns \c true if the exchange was found and removed, \c false otherwise.
787*/
788bool QCoapProtocolPrivate::forgetExchange(const QCoapToken &token)
789{
790 return exchangeMap.remove(key: token) > 0;
791}
792
793/*!
794 \internal
795
796 Remove the exchange using a request.
797
798 \sa forgetExchange(const QCoapToken &)
799*/
800bool QCoapProtocolPrivate::forgetExchange(const QCoapInternalRequest *request)
801{
802 return forgetExchange(token: request->token());
803}
804
805/*!
806 \internal
807
808 Remove all replies for the exchange corresponding to \a token.
809*/
810bool QCoapProtocolPrivate::forgetExchangeReplies(const QCoapToken &token)
811{
812 auto it = exchangeMap.find(key: token);
813 if (it == exchangeMap.end())
814 return false;
815
816 it->replies.clear();
817 return true;
818}
819
820/*!
821 \internal
822
823 Returns \c true if the \a token is reserved or in use; returns \c false if
824 this token can be used to identify a new exchange.
825*/
826bool QCoapProtocolPrivate::isTokenRegistered(const QCoapToken &token) const
827{
828 // Reserved for empty messages and uninitialized tokens
829 if (token == QByteArray())
830 return true;
831
832 return exchangeMap.contains(key: token);
833}
834
835/*!
836 \internal
837
838 Returns \c true if the \a request is present in a currently registered
839 exchange.
840*/
841bool QCoapProtocolPrivate::isRequestRegistered(const QCoapInternalRequest *request) const
842{
843 for (auto it = exchangeMap.constBegin(); it != exchangeMap.constEnd(); ++it) {
844 if (it->request.data() == request)
845 return true;
846 }
847
848 return false;
849}
850
851/*!
852 \internal
853
854 Returns \c true if a request has a message id equal to \a id, or if \a id
855 is reserved.
856*/
857bool QCoapProtocolPrivate::isMessageIdRegistered(quint16 id) const
858{
859 // Reserved for uninitialized message Id
860 if (id == 0)
861 return true;
862
863 for (auto it = exchangeMap.constBegin(); it != exchangeMap.constEnd(); ++it) {
864 if (it->request->message()->messageId() == id)
865 return true;
866 }
867
868 return false;
869}
870
871/*!
872 \internal
873
874 Returns the ACK_TIMEOUT value in milliseconds.
875 The default is 2000.
876
877 \sa minimumTimeout(), setAckTimeout()
878*/
879uint QCoapProtocol::ackTimeout() const
880{
881 Q_D(const QCoapProtocol);
882 return d->ackTimeout;
883}
884
885/*!
886 \internal
887
888 Returns the ACK_RANDOM_FACTOR value.
889 The default is 1.5.
890
891 \sa setAckRandomFactor()
892*/
893double QCoapProtocol::ackRandomFactor() const
894{
895 Q_D(const QCoapProtocol);
896 return d->ackRandomFactor;
897}
898
899/*!
900 \internal
901
902 Returns the MAX_RETRANSMIT value. This is the maximum number of
903 retransmissions of a message, before notifying a timeout error.
904 The default is 4.
905
906 \sa setMaximumRetransmitCount()
907*/
908uint QCoapProtocol::maximumRetransmitCount() const
909{
910 Q_D(const QCoapProtocol);
911 return d->maximumRetransmitCount;
912}
913
914/*!
915 \internal
916
917 Returns the maximum block size wanted.
918 The default is 0, which invites the server to choose the block size.
919
920 \sa setBlockSize()
921*/
922quint16 QCoapProtocol::blockSize() const
923{
924 Q_D(const QCoapProtocol);
925 return d->blockSize;
926}
927
928/*!
929 \internal
930
931 Returns the MAX_TRANSMIT_SPAN in milliseconds, as defined in
932 \l{https://tools.ietf.org/search/rfc7252#section-4.8.2}{RFC 7252}.
933
934 It is the maximum time from the first transmission of a Confirmable
935 message to its last retransmission.
936*/
937uint QCoapProtocol::maximumTransmitSpan() const
938{
939 return static_cast<uint>(ackTimeout()
940 * ((1u << maximumRetransmitCount()) - 1)
941 * ackRandomFactor());
942}
943
944/*!
945 \internal
946
947 Returns the MAX_TRANSMIT_WAIT in milliseconds, as defined in
948 \l{https://tools.ietf.org/search/rfc7252#section-4.8.2}{RFC 7252}.
949
950 It is the maximum time from the first transmission of a Confirmable
951 message to the time when the sender gives up on receiving an
952 acknowledgment or reset.
953*/
954uint QCoapProtocol::maximumTransmitWait() const
955{
956 return static_cast<uint>(ackTimeout() * ((1u << (maximumRetransmitCount() + 1)) - 1)
957 * ackRandomFactor());
958}
959
960/*!
961 \internal
962
963 Returns the MAX_LATENCY in milliseconds, as defined in
964 \l{https://tools.ietf.org/search/rfc7252#section-4.8.2}{RFC 7252}. This
965 value is arbitrarily set to 100 seconds by the standard.
966
967 It is the maximum time a datagram is expected to take from the start of
968 its transmission to the completion of its reception.
969*/
970uint QCoapProtocol::maximumLatency() const
971{
972 return 100 * 1000;
973}
974
975/*!
976 \internal
977
978 Returns the minimum duration for messages timeout. The timeout is defined
979 as a random value between minimumTimeout() and maximumTimeout(). This is a
980 convenience method identical to ackTimeout().
981
982 \sa ackTimeout(), setAckTimeout()
983*/
984uint QCoapProtocol::minimumTimeout() const
985{
986 Q_D(const QCoapProtocol);
987 return d->ackTimeout;
988}
989
990/*!
991 \internal
992
993 Returns the maximum duration for messages timeout in milliseconds.
994
995 \sa maximumTimeout(), setAckTimeout(), setAckRandomFactor()
996*/
997uint QCoapProtocol::maximumTimeout() const
998{
999 Q_D(const QCoapProtocol);
1000 return static_cast<uint>(d->ackTimeout * d->ackRandomFactor);
1001}
1002
1003/*!
1004 \internal
1005
1006 Returns the \c NON_LIFETIME in milliseconds, as defined in
1007 \l{https://tools.ietf.org/search/rfc7252#section-4.8.2}{RFC 7252}.
1008
1009 It is the time from sending a non-confirmable message to the time its
1010 message ID can be safely reused.
1011*/
1012uint QCoapProtocol::nonConfirmLifetime() const
1013{
1014 return maximumTransmitSpan() + maximumLatency();
1015}
1016
1017/*!
1018 \internal
1019
1020 Returns the \c MAX_SERVER_RESPONSE_DELAY in milliseconds, as defined in
1021 \l {RFC 7390 - Section 2.5}.
1022
1023 It is the expected maximum response delay over all servers that the client
1024 can send a multicast request to.
1025
1026 \sa setMaximumServerResponseDelay()
1027*/
1028uint QCoapProtocol::maximumServerResponseDelay() const
1029{
1030 Q_D(const QCoapProtocol);
1031 return d->maximumServerResponseDelay;
1032}
1033
1034/*!
1035 \internal
1036
1037 Sets the ACK_TIMEOUT value to \a ackTimeout in milliseconds.
1038 The default is 2000 ms.
1039
1040 Timeout only applies to Confirmable message. The actual timeout for
1041 reliable transmissions is a random value between ackTimeout() and
1042 ackTimeout() * ackRandomFactor().
1043
1044 \sa ackTimeout(), setAckRandomFactor(), minimumTimeout(), maximumTimeout()
1045*/
1046void QCoapProtocol::setAckTimeout(uint ackTimeout)
1047{
1048 Q_D(QCoapProtocol);
1049 d->ackTimeout = ackTimeout;
1050}
1051
1052/*!
1053 \internal
1054
1055 Sets the ACK_RANDOM_FACTOR value to \a ackRandomFactor. This value
1056 should be greater than or equal to 1.
1057 The default is 1.5.
1058
1059 \sa ackRandomFactor(), setAckTimeout()
1060*/
1061void QCoapProtocol::setAckRandomFactor(double ackRandomFactor)
1062{
1063 Q_D(QCoapProtocol);
1064 if (ackRandomFactor < 1)
1065 qCWarning(lcCoapProtocol, "The acknowledgment random factor should be >= 1");
1066
1067 d->ackRandomFactor = qMax(a: 1., b: ackRandomFactor);
1068}
1069
1070/*!
1071 \internal
1072
1073 Sets the MAX_RETRANSMIT value to \a maximumRetransmitCount, but never
1074 to more than 25.
1075 The default is 4.
1076
1077 \sa maximumRetransmitCount()
1078*/
1079void QCoapProtocol::setMaximumRetransmitCount(uint maximumRetransmitCount)
1080{
1081 Q_D(QCoapProtocol);
1082
1083 if (maximumRetransmitCount > 25) {
1084 qCWarning(lcCoapProtocol, "Maximum retransmit count is capped at 25.");
1085 maximumRetransmitCount = 25;
1086 }
1087
1088 d->maximumRetransmitCount = maximumRetransmitCount;
1089}
1090
1091/*!
1092 \internal
1093
1094 Sets the maximum block size wanted to \a blockSize.
1095
1096 The \a blockSize should be zero, or range from 16 to 1024 and be a
1097 power of 2. A size of 0 invites the server to choose the block size.
1098
1099 \sa blockSize()
1100*/
1101void QCoapProtocol::setBlockSize(quint16 blockSize)
1102{
1103 Q_D(QCoapProtocol);
1104
1105 if ((blockSize & (blockSize - 1)) != 0) {
1106 qCWarning(lcCoapProtocol, "Block size should be a power of 2");
1107 return;
1108 }
1109
1110 if (blockSize != 0 && (blockSize < 16 || blockSize > 1024)) {
1111 qCWarning(lcCoapProtocol, "Block size should be set to zero,"
1112 "or to a power of 2 from 16 through 1024");
1113 return;
1114 }
1115
1116 d->blockSize = blockSize;
1117}
1118
1119/*!
1120 \internal
1121
1122 Sets the \c MAX_SERVER_RESPONSE_DELAY value to \a responseDelay in milliseconds.
1123 The default is 250 seconds.
1124
1125 As defined in \l {RFC 7390 - Section 2.5}, \c MAX_SERVER_RESPONSE_DELAY is the expected
1126 maximum response delay over all servers that the client can send a multicast request to.
1127
1128 \sa maximumServerResponseDelay()
1129*/
1130void QCoapProtocol::setMaximumServerResponseDelay(uint responseDelay)
1131{
1132 Q_D(QCoapProtocol);
1133 d->maximumServerResponseDelay = responseDelay;
1134}
1135
1136/*!
1137 \internal
1138
1139 Sets the minimum token size to \a tokenSize in bytes. For security reasons it is
1140 recommended to use tokens with a length of at least 4 bytes. The default value for
1141 this parameter is 4 bytes.
1142*/
1143void QCoapProtocol::setMinimumTokenSize(int tokenSize)
1144{
1145 Q_D(QCoapProtocol);
1146
1147 if (tokenSize > 0 && tokenSize <= 8) {
1148 d->minimumTokenSize = tokenSize;
1149 } else {
1150 qCWarning(lcCoapProtocol,
1151 "Failed to set the minimum token size,"
1152 "it should not be more than 8 bytes and cannot be 0.");
1153 }
1154}
1155
1156QT_END_NAMESPACE
1157

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