Commit 313b3346c39903b76a835d8a211c0679191bf26b

Authored by Wiebe Cazemier
1 parent 9a34fc46

Connect/connack in mqtt5

CMakeLists.txt
... ... @@ -60,6 +60,7 @@ add_executable(FlashMQ
60 60 threadloop.h
61 61 publishcopyfactory.h
62 62 variablebyteint.h
  63 + mqtt5properties.h
63 64  
64 65 mainapp.cpp
65 66 main.cpp
... ... @@ -99,6 +100,7 @@ add_executable(FlashMQ
99 100 threadloop.cpp
100 101 publishcopyfactory.cpp
101 102 variablebyteint.cpp
  103 + mqtt5properties.cpp
102 104  
103 105 )
104 106  
... ...
forward_declarations.h
... ... @@ -26,6 +26,7 @@ class MqttPacket;
26 26 class SubscriptionStore;
27 27 class Session;
28 28 class Settings;
  29 +class Mqtt5PropertyBuilder;
29 30  
30 31  
31 32 #endif // FORWARD_DECLARATIONS_H
... ...
mqtt5properties.cpp 0 → 100644
  1 +#include "mqtt5properties.h"
  2 +
  3 +#include "cstring"
  4 +
  5 +#include "exceptions.h"
  6 +
  7 +Mqtt5PropertyBuilder::Mqtt5PropertyBuilder()
  8 +{
  9 + bites.reserve(128);
  10 +}
  11 +
  12 +size_t Mqtt5PropertyBuilder::getLength() const
  13 +{
  14 + return length.getLen() + bites.size();
  15 +}
  16 +
  17 +const VariableByteInt &Mqtt5PropertyBuilder::getVarInt() const
  18 +{
  19 + return length;
  20 +}
  21 +
  22 +const std::vector<char> &Mqtt5PropertyBuilder::getBites() const
  23 +{
  24 + return bites;
  25 +}
  26 +
  27 +void Mqtt5PropertyBuilder::writeSessionExpiry(uint32_t val)
  28 +{
  29 + writeUint32(Mqtt5Properties::SessionExpiryInterval, val);
  30 +}
  31 +
  32 +void Mqtt5PropertyBuilder::writeReceiveMax(uint16_t val)
  33 +{
  34 + writeUint16(Mqtt5Properties::ReceiveMaximum, val);
  35 +}
  36 +
  37 +void Mqtt5PropertyBuilder::writeRetainAvailable(uint8_t val)
  38 +{
  39 + writeUint8(Mqtt5Properties::RetainAvailable, val);
  40 +}
  41 +
  42 +void Mqtt5PropertyBuilder::writeMaxPacketSize(uint32_t val)
  43 +{
  44 + writeUint32(Mqtt5Properties::MaximumPacketSize, val);
  45 +}
  46 +
  47 +void Mqtt5PropertyBuilder::writeAssignedClientId(const std::string &clientid)
  48 +{
  49 + writeStr(Mqtt5Properties::AssignedClientIdentifier, clientid);
  50 +}
  51 +
  52 +void Mqtt5PropertyBuilder::writeMaxTopicAliases(uint16_t val)
  53 +{
  54 + writeUint16(Mqtt5Properties::TopicAliasMaximum, val);
  55 +}
  56 +
  57 +void Mqtt5PropertyBuilder::writeWildcardSubscriptionAvailable(uint8_t val)
  58 +{
  59 + writeUint8(Mqtt5Properties::WildcardSubscriptionAvailable, val);
  60 +}
  61 +
  62 +void Mqtt5PropertyBuilder::writeSharedSubscriptionAvailable(uint8_t val)
  63 +{
  64 + writeUint8(Mqtt5Properties::SharedSubscriptionAvailable, val);
  65 +}
  66 +
  67 +void Mqtt5PropertyBuilder::writeUint32(Mqtt5Properties prop, const uint32_t x)
  68 +{
  69 + size_t pos = bites.size();
  70 + const size_t newSize = pos + 5;
  71 + bites.resize(newSize);
  72 + this->length = newSize;
  73 +
  74 + const uint8_t a = static_cast<uint8_t>(x >> 24);
  75 + const uint8_t b = static_cast<uint8_t>(x >> 16);
  76 + const uint8_t c = static_cast<uint8_t>(x >> 8);
  77 + const uint8_t d = static_cast<uint8_t>(x);
  78 +
  79 + bites[pos++] = static_cast<uint8_t>(prop);
  80 + bites[pos++] = a;
  81 + bites[pos++] = b;
  82 + bites[pos++] = c;
  83 + bites[pos] = d;
  84 +}
  85 +
  86 +void Mqtt5PropertyBuilder::writeUint16(Mqtt5Properties prop, const uint16_t x)
  87 +{
  88 + size_t pos = bites.size();
  89 + const size_t newSize = pos + 3;
  90 + bites.resize(newSize);
  91 + this->length = newSize;
  92 +
  93 + const uint8_t a = static_cast<uint8_t>(x >> 8);
  94 + const uint8_t b = static_cast<uint8_t>(x);
  95 +
  96 + bites[pos++] = static_cast<uint8_t>(prop);
  97 + bites[pos++] = a;
  98 + bites[pos] = b;
  99 +}
  100 +
  101 +void Mqtt5PropertyBuilder::writeUint8(Mqtt5Properties prop, const uint8_t x)
  102 +{
  103 + size_t pos = bites.size();
  104 + const size_t newSize = pos + 2;
  105 + bites.resize(newSize);
  106 + this->length = newSize;
  107 +
  108 + bites[pos++] = static_cast<uint8_t>(prop);
  109 + bites[pos] = x;
  110 +}
  111 +
  112 +void Mqtt5PropertyBuilder::writeStr(Mqtt5Properties prop, const std::string &str)
  113 +{
  114 + if (str.length() > 65535)
  115 + throw ProtocolError("String too long.");
  116 +
  117 + const uint16_t strlen = str.length();
  118 +
  119 + size_t pos = bites.size();
  120 + const size_t newSize = pos + strlen + 2;
  121 + bites.resize(newSize);
  122 + this->length = newSize;
  123 +
  124 + const uint8_t a = static_cast<uint8_t>(strlen >> 8);
  125 + const uint8_t b = static_cast<uint8_t>(strlen);
  126 +
  127 + bites[pos++] = static_cast<uint8_t>(prop);
  128 + bites[pos++] = a;
  129 + bites[pos++] = b;
  130 +
  131 + std::memcpy(&bites[pos], str.c_str(), strlen);
  132 +}
  133 +
... ...
mqtt5properties.h 0 → 100644
  1 +#ifndef MQTT5PROPERTIES_H
  2 +#define MQTT5PROPERTIES_H
  3 +
  4 +#include <vector>
  5 +
  6 +#include "types.h"
  7 +#include "variablebyteint.h"
  8 +
  9 +class Mqtt5PropertyBuilder
  10 +{
  11 + std::vector<char> bites;
  12 + VariableByteInt length;
  13 +
  14 + void writeUint32(Mqtt5Properties prop, const uint32_t x);
  15 + void writeUint16(Mqtt5Properties prop, const uint16_t x);
  16 + void writeUint8(Mqtt5Properties prop, const uint8_t x);
  17 + void writeStr(Mqtt5Properties prop, const std::string &str);
  18 +public:
  19 + Mqtt5PropertyBuilder();
  20 +
  21 + size_t getLength() const;
  22 + const VariableByteInt &getVarInt() const;
  23 + const std::vector<char> &getBites() const;
  24 +
  25 + void writeSessionExpiry(uint32_t val);
  26 + void writeReceiveMax(uint16_t val);
  27 + void writeRetainAvailable(uint8_t val);
  28 + void writeMaxPacketSize(uint32_t val);
  29 + void writeAssignedClientId(const std::string &clientid);
  30 + void writeMaxTopicAliases(uint16_t val);
  31 + void writeWildcardSubscriptionAvailable(uint8_t val);
  32 + void writeSharedSubscriptionAvailable(uint8_t val);
  33 +};
  34 +
  35 +#endif // MQTT5PROPERTIES_H
... ...
mqttpacket.cpp
... ... @@ -110,17 +110,20 @@ std::shared_ptr&lt;MqttPacket&gt; MqttPacket::getCopy(char new_max_qos) const
110 110 return copyPacket;
111 111 }
112 112  
113   -// This constructor cheats and doesn't use calculateRemainingLength, because it's always the same. It allocates enough space in the vector.
114 113 MqttPacket::MqttPacket(const ConnAck &connAck) :
115   - bites(connAck.getLengthWithoutFixedHeader() + 2)
  114 + bites(connAck.getLengthWithoutFixedHeader())
116 115 {
117   - fixed_header_length = 2;
118 116 packetType = PacketType::CONNACK;
119 117 first_byte = static_cast<char>(packetType) << 4;
120   - writeByte(first_byte);
121   - writeByte(2); // length is always 2.
122 118 writeByte(connAck.session_present & 0b00000001); // all connect-ack flags are 0, except session-present. [MQTT-3.2.2.1]
123   - writeByte(static_cast<char>(connAck.return_code));
  119 + writeByte(connAck.return_code);
  120 +
  121 + if (connAck.protocol_version >= ProtocolVersion::Mqtt5)
  122 + {
  123 + writeProperties(connAck.propertyBuilder);
  124 + }
  125 +
  126 + calculateRemainingLength();
124 127 }
125 128  
126 129 MqttPacket::MqttPacket(const SubAck &subAck) :
... ... @@ -343,7 +346,10 @@ void MqttPacket::handleConnect()
343 346 }
344 347 else
345 348 {
346   - ConnAck connAck(ConnAckReturnCodes::UnacceptableProtocolVersion);
  349 + // The specs are unclear when to use the version 3 codes or version 5 codes.
  350 + ProtocolVersion fuzzyProtocolVersion = protocol_level < 0x05 ? ProtocolVersion::Mqtt31 : ProtocolVersion::Mqtt5;
  351 +
  352 + ConnAck connAck(fuzzyProtocolVersion, ReasonCodes::UnsupportedProtocolVersion);
347 353 MqttPacket response(connAck);
348 354 sender->setReadyForDisconnect();
349 355 sender->writeMqttPacket(response);
... ... @@ -398,7 +404,7 @@ void MqttPacket::handleConnect()
398 404 max_packet_size = std::min<uint32_t>(readFourBytesToUint32(), max_packet_size);
399 405 break;
400 406 case Mqtt5Properties::TopicAliasMaximum:
401   - max_topic_aliases = readTwoBytesToUInt16();
  407 + max_topic_aliases = std::min<uint16_t>(readTwoBytesToUInt16(), max_topic_aliases);
402 408 break;
403 409 case Mqtt5Properties::RequestResponseInformation:
404 410 request_response_information = !!readByte();
... ... @@ -451,7 +457,7 @@ void MqttPacket::handleConnect()
451 457 // The specs don't really say what to do when client id not UTF8, so including here.
452 458 if (!isValidUtf8(client_id) || !isValidUtf8(username) || !isValidUtf8(password) || !isValidUtf8(will_topic))
453 459 {
454   - ConnAck connAck(ConnAckReturnCodes::MalformedUsernameOrPassword);
  460 + ConnAck connAck(protocolVersion, ReasonCodes::BadUserNameOrPassword);
455 461 MqttPacket response(connAck);
456 462 sender->setReadyForDisconnect();
457 463 sender->writeMqttPacket(response);
... ... @@ -480,7 +486,7 @@ void MqttPacket::handleConnect()
480 486  
481 487 if (!validClientId)
482 488 {
483   - ConnAck connAck(ConnAckReturnCodes::ClientIdRejected);
  489 + ConnAck connAck(protocolVersion, ReasonCodes::ClientIdentifierNotValid);
484 490 MqttPacket response(connAck);
485 491 sender->setDisconnectReason("Invalid clientID");
486 492 sender->setReadyForDisconnect();
... ... @@ -488,9 +494,11 @@ void MqttPacket::handleConnect()
488 494 return;
489 495 }
490 496  
  497 + bool clientIdGenerated = false;
491 498 if (client_id.empty())
492 499 {
493 500 client_id = getSecureRandomString(23);
  501 + clientIdGenerated = true;
494 502 }
495 503  
496 504 sender->setClientProperties(protocolVersion, client_id, username, true, keep_alive, max_packet_size, max_topic_aliases);
... ... @@ -521,7 +529,22 @@ void MqttPacket::handleConnect()
521 529 bool sessionPresent = protocolVersion >= ProtocolVersion::Mqtt311 && !clean_start && subscriptionStore->sessionPresent(client_id);
522 530  
523 531 sender->setAuthenticated(true);
524   - ConnAck connAck(ConnAckReturnCodes::Accepted, sessionPresent);
  532 + ConnAck connAck(protocolVersion, ReasonCodes::Success, sessionPresent);
  533 +
  534 + if (protocolVersion >= ProtocolVersion::Mqtt5)
  535 + {
  536 + connAck.propertyBuilder = std::make_shared<Mqtt5PropertyBuilder>();
  537 + connAck.propertyBuilder->writeSessionExpiry(session_expire);
  538 + connAck.propertyBuilder->writeReceiveMax(max_qos_packets);
  539 + connAck.propertyBuilder->writeRetainAvailable(1);
  540 + connAck.propertyBuilder->writeMaxPacketSize(max_packet_size);
  541 + if (clientIdGenerated)
  542 + connAck.propertyBuilder->writeAssignedClientId(client_id);
  543 + connAck.propertyBuilder->writeMaxTopicAliases(max_topic_aliases);
  544 + connAck.propertyBuilder->writeWildcardSubscriptionAvailable(1);
  545 + connAck.propertyBuilder->writeSharedSubscriptionAvailable(0);
  546 + }
  547 +
525 548 MqttPacket response(connAck);
526 549 sender->writeMqttPacket(response);
527 550 logger->logf(LOG_NOTICE, "Client '%s' logged in successfully", sender->repr().c_str());
... ... @@ -530,7 +553,7 @@ void MqttPacket::handleConnect()
530 553 }
531 554 else
532 555 {
533   - ConnAck connDeny(ConnAckReturnCodes::NotAuthorized, false);
  556 + ConnAck connDeny(protocolVersion, ReasonCodes::NotAuthorized, false);
534 557 MqttPacket response(connDeny);
535 558 sender->setDisconnectReason("Access denied");
536 559 sender->setReadyForDisconnect();
... ... @@ -960,6 +983,23 @@ void MqttPacket::writeBytes(const char *b, size_t len)
960 983 pos += len;
961 984 }
962 985  
  986 +void MqttPacket::writeProperties(const std::shared_ptr<Mqtt5PropertyBuilder> &properties)
  987 +{
  988 + if (!properties)
  989 + writeByte(0);
  990 + else
  991 + {
  992 + writeVariableByteInt(properties->getVarInt());
  993 + const std::vector<char> &b = properties->getBites();
  994 + writeBytes(b.data(), b.size());
  995 + }
  996 +}
  997 +
  998 +void MqttPacket::writeVariableByteInt(const VariableByteInt &v)
  999 +{
  1000 + writeBytes(v.data(), v.getLen());
  1001 +}
  1002 +
963 1003 uint16_t MqttPacket::readTwoBytesToUInt16()
964 1004 {
965 1005 if (pos + 2 > bites.size())
... ...
mqttpacket.h
... ... @@ -34,6 +34,7 @@ License along with FlashMQ. If not, see &lt;https://www.gnu.org/licenses/&gt;.
34 34 #include "mainapp.h"
35 35  
36 36 #include "variablebyteint.h"
  37 +#include "mqtt5properties.h"
37 38  
38 39 class MqttPacket
39 40 {
... ... @@ -62,6 +63,8 @@ class MqttPacket
62 63 void writeByte(char b);
63 64 void writeUint16(uint16_t x);
64 65 void writeBytes(const char *b, size_t len);
  66 + void writeProperties(const std::shared_ptr<Mqtt5PropertyBuilder> &properties);
  67 + void writeVariableByteInt(const VariableByteInt &v);
65 68 uint16_t readTwoBytesToUInt16();
66 69 uint32_t readFourBytesToUint32();
67 70 size_t remainingAfterPos();
... ...
types.cpp
... ... @@ -18,14 +18,66 @@ License along with FlashMQ. If not, see &lt;https://www.gnu.org/licenses/&gt;.
18 18 #include "cassert"
19 19  
20 20 #include "types.h"
  21 +#include "mqtt5properties.h"
21 22  
22   -ConnAck::ConnAck(ConnAckReturnCodes return_code, bool session_present) :
23   - return_code(return_code),
  23 +ConnAck::ConnAck(const ProtocolVersion protVersion, ReasonCodes return_code, bool session_present) :
  24 + protocol_version(protVersion),
24 25 session_present(session_present)
25 26 {
26   - // [MQTT-3.2.2-4]
27   - if (return_code > ConnAckReturnCodes::Accepted)
28   - session_present = false;
  27 +
  28 + if (this->protocol_version <= ProtocolVersion::Mqtt311)
  29 + {
  30 + ConnAckReturnCodes mqtt3_return = ConnAckReturnCodes::Accepted;
  31 +
  32 + switch (return_code)
  33 + {
  34 + case ReasonCodes::Success:
  35 + mqtt3_return = ConnAckReturnCodes::Accepted;
  36 + break;
  37 + case ReasonCodes::UnsupportedProtocolVersion:
  38 + mqtt3_return = ConnAckReturnCodes::UnacceptableProtocolVersion;
  39 + break;
  40 + case ReasonCodes::ClientIdentifierNotValid:
  41 + mqtt3_return = ConnAckReturnCodes::ClientIdRejected;
  42 + break;
  43 + case ReasonCodes::ServerUnavailable:
  44 + mqtt3_return = ConnAckReturnCodes::ServerUnavailable;
  45 + break;
  46 + case ReasonCodes::BadUserNameOrPassword:
  47 + mqtt3_return = ConnAckReturnCodes::MalformedUsernameOrPassword;
  48 + break;
  49 + case ReasonCodes::NotAuthorized:
  50 + mqtt3_return = ConnAckReturnCodes::NotAuthorized;
  51 + default:
  52 + assert(false);
  53 + }
  54 +
  55 + // [MQTT-3.2.2-4]
  56 + if (mqtt3_return > ConnAckReturnCodes::Accepted)
  57 + session_present = false;
  58 +
  59 + this->return_code = static_cast<uint8_t>(mqtt3_return);
  60 + }
  61 + else
  62 + {
  63 + this->return_code = static_cast<uint8_t>(return_code);
  64 +
  65 + // MQTT-3.2.2-6
  66 + if (this->return_code > 0)
  67 + session_present = false;
  68 + }
  69 +}
  70 +
  71 +size_t ConnAck::getLengthWithoutFixedHeader() const
  72 +{
  73 + size_t result = 2;
  74 +
  75 + if (this->protocol_version >= ProtocolVersion::Mqtt5)
  76 + {
  77 + const size_t proplen = propertyBuilder ? propertyBuilder->getLength() : 1;
  78 + result += proplen;
  79 + }
  80 + return result;
29 81 }
30 82  
31 83 SubAck::SubAck(uint16_t packet_id, const std::list<char> &subs_qos_reponses) :
... ...
... ... @@ -21,6 +21,9 @@ License along with FlashMQ. If not, see &lt;https://www.gnu.org/licenses/&gt;.
21 21 #include "stdint.h"
22 22 #include <list>
23 23 #include <string>
  24 +#include <memory>
  25 +
  26 +#include "forward_declarations.h"
24 27  
25 28 enum class PacketType
26 29 {
... ... @@ -83,6 +86,9 @@ enum class Mqtt5Properties
83 86 SharedSubscriptionAvailable = 42
84 87 };
85 88  
  89 +/**
  90 + * @brief The ConnAckReturnCodes enum are for MQTT3
  91 + */
86 92 enum class ConnAckReturnCodes
87 93 {
88 94 Accepted = 0,
... ... @@ -93,13 +99,67 @@ enum class ConnAckReturnCodes
93 99 NotAuthorized = 5
94 100 };
95 101  
  102 +/**
  103 + * @brief The ReasonCodes enum are for MQTT5.
  104 + */
  105 +enum class ReasonCodes
  106 +{
  107 + Success = 0,
  108 + GrantedQoS0 = 0,
  109 + GrantedQoS1 = 1,
  110 + GrantedQoS2 = 2,
  111 + DisconnectWithWill = 4,
  112 + NoMatchingSubscribers = 16,
  113 + NoSubscriptionExisted = 17,
  114 + ContinueAuthentication = 24,
  115 + ReAuthenticate = 25,
  116 + UnspecifiedError = 128,
  117 + MalformedPacket = 129,
  118 + ProtocolError = 130,
  119 + ImplementationSpecificError = 131,
  120 + UnsupportedProtocolVersion = 132,
  121 + ClientIdentifierNotValid = 133,
  122 + BadUserNameOrPassword = 134,
  123 + NotAuthorized = 135,
  124 + ServerUnavailable = 136,
  125 + ServerBusy = 137,
  126 + Banned = 138,
  127 + ServerShuttingDown = 139,
  128 + BadAuthenticationMethod = 140,
  129 + KeepAliveTimeout = 141,
  130 + SessionTakenOver = 142,
  131 + TopicFilterInvalid = 143,
  132 + TopicNameInvalid = 144,
  133 + PacketIdentifierInUse = 145,
  134 + ReceiveMaximumExceeded = 147,
  135 + TopicAliasInvalid = 148,
  136 + PacketTooLarge = 149,
  137 + MessageRateTooHigh = 150,
  138 + QuoteExceeded = 151,
  139 + AdministrativeAction = 152,
  140 + PayloadFormatInvalid = 153,
  141 + RetainNotSupported = 154,
  142 + QosNotSupported = 155,
  143 + UseAnotherServer = 156,
  144 + ServerMoved = 157,
  145 + SharedSubscriptionsNotSupported = 158,
  146 + ConnectionRateExceeded = 159,
  147 + MaximumConnectTime = 160,
  148 + SubscriptionIdentifiersNotSupported = 161,
  149 + WildcardSubscriptionsNotSupported = 162
  150 +};
  151 +
96 152 class ConnAck
97 153 {
98 154 public:
99   - ConnAck(ConnAckReturnCodes return_code, bool session_present=false);
100   - ConnAckReturnCodes return_code;
  155 + ConnAck(const ProtocolVersion protVersion, ReasonCodes return_code, bool session_present=false);
  156 +
  157 + const ProtocolVersion protocol_version;
  158 + uint8_t return_code;
101 159 bool session_present = false;
102   - size_t getLengthWithoutFixedHeader() const { return 2;} // size of connack is always the same
  160 + std::shared_ptr<Mqtt5PropertyBuilder> propertyBuilder;
  161 +
  162 + size_t getLengthWithoutFixedHeader() const;
103 163 };
104 164  
105 165 enum class SubAckReturnCodes
... ...
variablebyteint.cpp
... ... @@ -34,3 +34,8 @@ uint8_t VariableByteInt::getLen() const
34 34 {
35 35 return len;
36 36 }
  37 +
  38 +const char *VariableByteInt::data() const
  39 +{
  40 + return &bytes[0];
  41 +}
... ...
variablebyteint.h
... ... @@ -12,6 +12,7 @@ public:
12 12 void readIntoBuf(CirBuf &buf) const;
13 13 VariableByteInt &operator=(uint32_t x);
14 14 uint8_t getLen() const;
  15 + const char *data() const;
15 16 };
16 17  
17 18 #endif // VARIABLEBYTEINT_H
... ...