Fix parsing error of AUTH packet with empty body (#2070)

* Reformat code

* Fix issue

* Fix AUTH packet serialization
This commit is contained in:
Christian
2024-09-07 09:49:10 +02:00
committed by GitHub
parent c4eb05105b
commit 2fdbb02dc8
5 changed files with 874 additions and 919 deletions
@@ -4,32 +4,36 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using MQTTnet.Formatter; using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Tests.Formatter namespace MQTTnet.Tests.Formatter;
{
[TestClass] [TestClass]
public sealed class MqttPacketSerialization_V5_Tests public sealed class MqttPacketSerialization_V5_Tests
{ {
[TestMethod]
public void Empty_Auth_Packet_Is_Success()
{
var buffer = MqttPacketSerializationHelper.EncodePacket(new MqttAuthPacket(), MqttProtocolVersion.V500);
var packet = MqttPacketSerializationHelper.DecodePacket(buffer, MqttProtocolVersion.V500);
Assert.IsNotNull(packet);
Assert.IsInstanceOfType<MqttAuthPacket>(packet);
}
[TestMethod] [TestMethod]
public void Serialize_Full_MqttAuthPacket_V500() public void Serialize_Full_MqttAuthPacket_V500()
{ {
var authPacket = new MqttAuthPacket var authPacket = new MqttAuthPacket
{ {
AuthenticationData = Encoding.UTF8.GetBytes("AuthenticationData"), AuthenticationData = "AuthenticationData"u8.ToArray(),
AuthenticationMethod = "AuthenticationMethod", AuthenticationMethod = "AuthenticationMethod",
ReasonCode = MqttAuthenticateReasonCode.ContinueAuthentication, ReasonCode = MqttAuthenticateReasonCode.ContinueAuthentication,
ReasonString = "ReasonString", ReasonString = "ReasonString",
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(authPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(authPacket, MqttProtocolVersion.V500);
@@ -46,7 +50,7 @@ namespace MQTTnet.Tests.Formatter
{ {
var connAckPacket = new MqttConnAckPacket var connAckPacket = new MqttConnAckPacket
{ {
AuthenticationData = Encoding.UTF8.GetBytes("AuthenticationData"), AuthenticationData = "AuthenticationData"u8.ToArray(),
AuthenticationMethod = "AuthenticationMethod", AuthenticationMethod = "AuthenticationMethod",
ReasonCode = MqttConnectReasonCode.ServerUnavailable, ReasonCode = MqttConnectReasonCode.ServerUnavailable,
ReasonString = "ReasonString", ReasonString = "ReasonString",
@@ -65,10 +69,7 @@ namespace MQTTnet.Tests.Formatter
SubscriptionIdentifiersAvailable = true, SubscriptionIdentifiersAvailable = true,
TopicAliasMaximum = 963, TopicAliasMaximum = 963,
WildcardSubscriptionAvailable = true, WildcardSubscriptionAvailable = true,
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connAckPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connAckPacket, MqttProtocolVersion.V500);
@@ -101,15 +102,15 @@ namespace MQTTnet.Tests.Formatter
var connectPacket = new MqttConnectPacket var connectPacket = new MqttConnectPacket
{ {
Username = "Username", Username = "Username",
Password = Encoding.UTF8.GetBytes("Password"), Password = "Password"u8.ToArray(),
ClientId = "ClientId", ClientId = "ClientId",
AuthenticationData = Encoding.UTF8.GetBytes("AuthenticationData"), AuthenticationData = "AuthenticationData"u8.ToArray(),
AuthenticationMethod = "AuthenticationMethod", AuthenticationMethod = "AuthenticationMethod",
CleanSession = true, CleanSession = true,
ReceiveMaximum = 123, ReceiveMaximum = 123,
WillFlag = true, WillFlag = true,
WillTopic = "WillTopic", WillTopic = "WillTopic",
WillMessage = Encoding.UTF8.GetBytes("WillMessage"), WillMessage = "WillMessage"u8.ToArray(),
WillRetain = true, WillRetain = true,
KeepAlivePeriod = 456, KeepAlivePeriod = 456,
MaximumPacketSize = 789, MaximumPacketSize = 789,
@@ -118,20 +119,14 @@ namespace MQTTnet.Tests.Formatter
SessionExpiryInterval = 27, SessionExpiryInterval = 27,
TopicAliasMaximum = 67, TopicAliasMaximum = 67,
WillContentType = "WillContentType", WillContentType = "WillContentType",
WillCorrelationData = Encoding.UTF8.GetBytes("WillCorrelationData"), WillCorrelationData = "WillCorrelationData"u8.ToArray(),
WillDelayInterval = 782, WillDelayInterval = 782,
WillQoS = MqttQualityOfServiceLevel.ExactlyOnce, WillQoS = MqttQualityOfServiceLevel.ExactlyOnce,
WillResponseTopic = "WillResponseTopic", WillResponseTopic = "WillResponseTopic",
WillMessageExpiryInterval = 542, WillMessageExpiryInterval = 542,
WillPayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData, WillPayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData,
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")],
{ WillUserProperties = [new MqttUserProperty("WillFoo", "WillBar")]
new MqttUserProperty("Foo", "Bar")
},
WillUserProperties = new List<MqttUserProperty>
{
new MqttUserProperty("WillFoo", "WillBar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connectPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connectPacket, MqttProtocolVersion.V500);
@@ -173,10 +168,7 @@ namespace MQTTnet.Tests.Formatter
ReasonString = "ReasonString", ReasonString = "ReasonString",
ServerReference = "ServerReference", ServerReference = "ServerReference",
SessionExpiryInterval = 234, SessionExpiryInterval = 234,
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(disconnectPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(disconnectPacket, MqttProtocolVersion.V500);
@@ -216,10 +208,7 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
ReasonCode = MqttPubAckReasonCode.NoMatchingSubscribers, ReasonCode = MqttPubAckReasonCode.NoMatchingSubscribers,
ReasonString = "ReasonString", ReasonString = "ReasonString",
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubAckPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubAckPacket, MqttProtocolVersion.V500);
@@ -238,10 +227,7 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
ReasonCode = MqttPubCompReasonCode.PacketIdentifierNotFound, ReasonCode = MqttPubCompReasonCode.PacketIdentifierNotFound,
ReasonString = "ReasonString", ReasonString = "ReasonString",
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubCompPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubCompPacket, MqttProtocolVersion.V500);
@@ -260,23 +246,17 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
Dup = true, Dup = true,
Retain = true, Retain = true,
PayloadSegment = new ArraySegment<byte>(Encoding.ASCII.GetBytes("Payload")), PayloadSegment = new ArraySegment<byte>("Payload"u8.ToArray()),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
Topic = "Topic", Topic = "Topic",
ResponseTopic = "/Response", ResponseTopic = "/Response",
ContentType = "Content-Type", ContentType = "Content-Type",
CorrelationData = Encoding.UTF8.GetBytes("CorrelationData"), CorrelationData = "CorrelationData"u8.ToArray(),
TopicAlias = 27, TopicAlias = 27,
SubscriptionIdentifiers = new List<uint> SubscriptionIdentifiers = [123],
{
123
},
MessageExpiryInterval = 38, MessageExpiryInterval = 38,
PayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData, PayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData,
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(publishPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(publishPacket, MqttProtocolVersion.V500);
@@ -305,10 +285,7 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
ReasonCode = MqttPubRecReasonCode.UnspecifiedError, ReasonCode = MqttPubRecReasonCode.UnspecifiedError,
ReasonString = "ReasonString", ReasonString = "ReasonString",
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRecPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRecPacket, MqttProtocolVersion.V500);
@@ -327,10 +304,7 @@ namespace MQTTnet.Tests.Formatter
PacketIdentifier = 123, PacketIdentifier = 123,
ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound, ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound,
ReasonString = "ReasonString", ReasonString = "ReasonString",
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRelPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRelPacket, MqttProtocolVersion.V500);
@@ -348,14 +322,8 @@ namespace MQTTnet.Tests.Formatter
{ {
PacketIdentifier = 123, PacketIdentifier = 123,
ReasonString = "ReasonString", ReasonString = "ReasonString",
ReasonCodes = new List<MqttSubscribeReasonCode> ReasonCodes = [MqttSubscribeReasonCode.GrantedQoS1],
{ UserProperties = [new MqttUserProperty("Foo", "Bar")]
MqttSubscribeReasonCode.GrantedQoS1
},
UserProperties = new List<MqttUserProperty>
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subAckPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subAckPacket, MqttProtocolVersion.V500);
@@ -374,8 +342,8 @@ namespace MQTTnet.Tests.Formatter
{ {
PacketIdentifier = 123, PacketIdentifier = 123,
SubscriptionIdentifier = 456, SubscriptionIdentifier = 456,
TopicFilters = new List<MqttTopicFilter> TopicFilters =
{ [
new MqttTopicFilter new MqttTopicFilter
{ {
Topic = "Topic", Topic = "Topic",
@@ -384,11 +352,8 @@ namespace MQTTnet.Tests.Formatter
RetainAsPublished = true, RetainAsPublished = true,
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
} }
}, ],
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subscribePacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subscribePacket, MqttProtocolVersion.V500);
@@ -410,15 +375,9 @@ namespace MQTTnet.Tests.Formatter
var unsubAckPacket = new MqttUnsubAckPacket var unsubAckPacket = new MqttUnsubAckPacket
{ {
PacketIdentifier = 123, PacketIdentifier = 123,
ReasonCodes = new List<MqttUnsubscribeReasonCode> ReasonCodes = [MqttUnsubscribeReasonCode.ImplementationSpecificError],
{
MqttUnsubscribeReasonCode.ImplementationSpecificError
},
ReasonString = "ReasonString", ReasonString = "ReasonString",
UserProperties = new List<MqttUserProperty> UserProperties = [new MqttUserProperty("Foo", "Bar")]
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubAckPacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubAckPacket, MqttProtocolVersion.V500);
@@ -436,14 +395,8 @@ namespace MQTTnet.Tests.Formatter
var unsubscribePacket = new MqttUnsubscribePacket var unsubscribePacket = new MqttUnsubscribePacket
{ {
PacketIdentifier = 123, PacketIdentifier = 123,
TopicFilters = new List<string> TopicFilters = ["TopicFilter1"],
{ UserProperties = [new MqttUserProperty("Foo", "Bar")]
"TopicFilter1"
},
UserProperties = new List<MqttUserProperty>
{
new MqttUserProperty("Foo", "Bar")
}
}; };
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubscribePacket, MqttProtocolVersion.V500); var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubscribePacket, MqttProtocolVersion.V500);
@@ -454,4 +407,3 @@ namespace MQTTnet.Tests.Formatter
CollectionAssert.AreEqual(unsubscribePacket.UserProperties, deserialized.UserProperties); CollectionAssert.AreEqual(unsubscribePacket.UserProperties, deserialized.UserProperties);
} }
} }
}
@@ -6,8 +6,8 @@ using MQTTnet.Formatter;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Tests.Mockups; using MQTTnet.Tests.Mockups;
namespace MQTTnet.Tests namespace MQTTnet.Tests;
{
public sealed class MqttPacketSerializationHelper : IDisposable public sealed class MqttPacketSerializationHelper : IDisposable
{ {
readonly IMqttPacketFormatter _packetFormatter; readonly IMqttPacketFormatter _packetFormatter;
@@ -27,13 +27,17 @@ namespace MQTTnet.Tests
public MqttPacket Decode(MqttPacketBuffer buffer) public MqttPacket Decode(MqttPacketBuffer buffer)
{ {
using (var channel = new MemoryMqttChannel(buffer.ToArray())) using var channel = new MemoryMqttChannel(buffer.ToArray());
{
var formatterAdapter = new MqttPacketFormatterAdapter(_protocolVersion, new MqttBufferWriter(4096, 65535)); var formatterAdapter = new MqttPacketFormatterAdapter(_protocolVersion, new MqttBufferWriter(4096, 65535));
var adapter = new MqttChannelAdapter(channel, formatterAdapter, MqttNetNullLogger.Instance); var adapter = new MqttChannelAdapter(channel, formatterAdapter, MqttNetNullLogger.Instance);
return adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult(); return adapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
} }
public static MqttPacket DecodePacket(byte[] buffer, MqttProtocolVersion protocolVersion)
{
using var helper = new MqttPacketSerializationHelper(protocolVersion);
return helper.Decode(new MqttPacketBuffer(buffer));
} }
public void Dispose() public void Dispose()
@@ -47,19 +51,14 @@ namespace MQTTnet.Tests
public static TPacket EncodeAndDecodePacket<TPacket>(TPacket packet, MqttProtocolVersion protocolVersion) where TPacket : MqttPacket public static TPacket EncodeAndDecodePacket<TPacket>(TPacket packet, MqttProtocolVersion protocolVersion) where TPacket : MqttPacket
{ {
using (var helper = new MqttPacketSerializationHelper(protocolVersion)) using var helper = new MqttPacketSerializationHelper(protocolVersion);
{
var buffer = helper.Encode(packet); var buffer = helper.Encode(packet);
return (TPacket)helper.Decode(buffer); return (TPacket)helper.Decode(buffer);
} }
}
public static byte[] EncodePacket(MqttPacket packet) public static byte[] EncodePacket(MqttPacket packet, MqttProtocolVersion protocolVersion)
{
using (var helper = new MqttPacketSerializationHelper())
{ {
using var helper = new MqttPacketSerializationHelper(protocolVersion);
return helper.Encode(packet).ToArray(); return helper.Encode(packet).ToArray();
} }
} }
}
}
@@ -68,12 +68,12 @@ namespace MQTTnet.Formatter.V5
MqttPacket DecodeAuthPacket(ArraySegment<byte> body) MqttPacket DecodeAuthPacket(ArraySegment<byte> body)
{ {
ThrowIfBodyIsEmpty(body);
_bufferReader.SetBuffer(body.Array, body.Offset, body.Count); _bufferReader.SetBuffer(body.Array, body.Offset, body.Count);
var packet = new MqttAuthPacket(); var packet = new MqttAuthPacket();
// MQTT spec: The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success) and there are no Properties.
// In this case the AUTH has a Remaining Length of 0.
if (_bufferReader.EndOfStream) if (_bufferReader.EndOfStream)
{ {
packet.ReasonCode = MqttAuthenticateReasonCode.Success; packet.ReasonCode = MqttAuthenticateReasonCode.Success;
@@ -9,14 +9,14 @@ using MQTTnet.Exceptions;
using MQTTnet.Packets; using MQTTnet.Packets;
using MQTTnet.Protocol; using MQTTnet.Protocol;
namespace MQTTnet.Formatter.V5 namespace MQTTnet.Formatter.V5;
{
public sealed class MqttV5PacketEncoder public sealed class MqttV5PacketEncoder
{ {
const int FixedHeaderSize = 1; const int FixedHeaderSize = 1;
readonly MqttBufferWriter _bufferWriter; readonly MqttBufferWriter _bufferWriter;
readonly MqttV5PropertiesWriter _propertiesWriter = new MqttV5PropertiesWriter(new MqttBufferWriter(1024, 4096)); readonly MqttV5PropertiesWriter _propertiesWriter = new(new MqttBufferWriter(1024, 4096));
public MqttV5PacketEncoder(MqttBufferWriter bufferWriter) public MqttV5PacketEncoder(MqttBufferWriter bufferWriter)
{ {
@@ -36,7 +36,7 @@ namespace MQTTnet.Formatter.V5
_bufferWriter.Seek(ReservedHeaderSize); _bufferWriter.Seek(ReservedHeaderSize);
var fixedHeader = EncodePacket(packet); var fixedHeader = EncodePacket(packet);
uint remainingLength = (uint)_bufferWriter.Length - ReservedHeaderSize; var remainingLength = (uint)_bufferWriter.Length - ReservedHeaderSize;
ReadOnlySequence<byte> payload = default; ReadOnlySequence<byte> payload = default;
if (packet is MqttPublishPacket publishPacket) if (packet is MqttPublishPacket publishPacket)
@@ -62,20 +62,25 @@ namespace MQTTnet.Formatter.V5
var buffer = _bufferWriter.GetBuffer(); var buffer = _bufferWriter.GetBuffer();
var firstSegment = new ArraySegment<byte>(buffer, headerOffset, _bufferWriter.Length - headerOffset); var firstSegment = new ArraySegment<byte>(buffer, headerOffset, _bufferWriter.Length - headerOffset);
return publishPacket == null return publishPacket == null ? new MqttPacketBuffer(firstSegment) : new MqttPacketBuffer(firstSegment, publishPacket.Payload);
? new MqttPacketBuffer(firstSegment)
: new MqttPacketBuffer(firstSegment, publishPacket.Payload);
} }
byte EncodeAuthPacket(MqttAuthPacket packet) byte EncodeAuthPacket(MqttAuthPacket packet)
{ {
_bufferWriter.WriteByte((byte)packet.ReasonCode);
_propertiesWriter.WriteAuthenticationMethod(packet.AuthenticationMethod); _propertiesWriter.WriteAuthenticationMethod(packet.AuthenticationMethod);
_propertiesWriter.WriteAuthenticationData(packet.AuthenticationData); _propertiesWriter.WriteAuthenticationData(packet.AuthenticationData);
_propertiesWriter.WriteReasonString(packet.ReasonString); _propertiesWriter.WriteReasonString(packet.ReasonString);
_propertiesWriter.WriteUserProperties(packet.UserProperties); _propertiesWriter.WriteUserProperties(packet.UserProperties);
// MQTT spec: The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success) and there are no Properties.
// In this case the AUTH has a Remaining Length of 0.
if (packet.ReasonCode == MqttAuthenticateReasonCode.Success && _propertiesWriter.Length == 0)
{
return MqttBufferWriter.BuildFixedHeader(MqttControlPacketType.Auth);
}
_bufferWriter.WriteByte((byte)packet.ReasonCode);
_propertiesWriter.WriteTo(_bufferWriter); _propertiesWriter.WriteTo(_bufferWriter);
_propertiesWriter.Reset(); _propertiesWriter.Reset();
@@ -539,4 +544,3 @@ namespace MQTTnet.Formatter.V5
} }
} }
} }
}