diff --git a/Build/MQTTnet.nuspec b/Build/MQTTnet.nuspec
index f232bafc..720cdb2b 100644
--- a/Build/MQTTnet.nuspec
+++ b/Build/MQTTnet.nuspec
@@ -13,7 +13,7 @@
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.
* [MQTTnet.Server] Moved server project to a dedicated GitHub repository.
-
+* [MQTTnet, MQTTnet.Extensions.ManagedClient] Fixed bug that allowed invalid subscriptions (Thanks to @marcelwinh).
Git commit: $gitCommit
Copyright Christian Kratky 2016-2020
diff --git a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs
index 931326bc..d349f81e 100644
--- a/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs
+++ b/Source/MQTTnet.Extensions.ManagedClient/ManagedMqttClient.cs
@@ -215,6 +215,11 @@ namespace MQTTnet.Extensions.ManagedClient
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
+ foreach (var topicFilter in topicFilters)
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
+ }
+
lock (_subscriptions)
{
foreach (var topicFilter in topicFilters)
diff --git a/Source/MQTTnet/Client/MqttClient.cs b/Source/MQTTnet/Client/MqttClient.cs
index 9dd7cb95..d519e78c 100644
--- a/Source/MQTTnet/Client/MqttClient.cs
+++ b/Source/MQTTnet/Client/MqttClient.cs
@@ -194,6 +194,11 @@ namespace MQTTnet.Client
{
if (options == null) throw new ArgumentNullException(nameof(options));
+ foreach (var topicFilter in options.TopicFilters)
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
+ }
+
ThrowIfDisposed();
ThrowIfNotConnected();
diff --git a/Source/MQTTnet/Protocol/MqttTopicValidator.cs b/Source/MQTTnet/Protocol/MqttTopicValidator.cs
index 00b16793..b8f02950 100644
--- a/Source/MQTTnet/Protocol/MqttTopicValidator.cs
+++ b/Source/MQTTnet/Protocol/MqttTopicValidator.cs
@@ -42,5 +42,15 @@ namespace MQTTnet.Protocol
}
}
}
+
+ public static void ThrowIfInvalidSubscribe(string topic)
+ {
+ if (string.IsNullOrEmpty(topic))
+ {
+ throw new MqttProtocolViolationException("Topic should not be empty.");
+ }
+
+ if (topic.IndexOf("#") != -1 && topic.IndexOf("#") != topic.Length - 1) throw new MqttProtocolViolationException("The character '#' is only allowed as last character");
+ }
}
}
diff --git a/Source/MQTTnet/Server/MqttServer.cs b/Source/MQTTnet/Server/MqttServer.cs
index 1dbe39a7..92ef34f4 100644
--- a/Source/MQTTnet/Server/MqttServer.cs
+++ b/Source/MQTTnet/Server/MqttServer.cs
@@ -113,7 +113,12 @@ namespace MQTTnet.Server
{
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
-
+
+ foreach (var topicFilter in topicFilters)
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe(topicFilter.Topic);
+ }
+
ThrowIfDisposed();
ThrowIfNotStarted();
diff --git a/Tests/MQTTnet.Core.Tests/MqttTopicValidatorSubscribe_Tests.cs b/Tests/MQTTnet.Core.Tests/MqttTopicValidatorSubscribe_Tests.cs
new file mode 100644
index 00000000..14797a0b
--- /dev/null
+++ b/Tests/MQTTnet.Core.Tests/MqttTopicValidatorSubscribe_Tests.cs
@@ -0,0 +1,54 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MQTTnet.Exceptions;
+using MQTTnet.Protocol;
+
+namespace MQTTnet.Tests
+{
+ [TestClass]
+ public class MqttTopicValidatorSubscribe_Tests
+ {
+ [TestMethod]
+ public void Valid_Topic()
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe("/a/b/c");
+ }
+
+ [TestMethod]
+ public void Valid_Topic_Plus_In_Between()
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe("/a/+/c");
+ }
+
+ [TestMethod]
+ public void Valid_Topic_Plus_Last_Char()
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe("/a/+");
+ }
+
+ [TestMethod]
+ public void Valid_Topic_Hash_Last_Char()
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe("/a/#");
+ }
+
+ [TestMethod]
+ public void Valid_Topic_Only_Hash()
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe("#");
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(MqttProtocolViolationException))]
+ public void Invalid_Topic_Hash_In_Between()
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe("/a/#/c");
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(MqttProtocolViolationException))]
+ public void Invalid_Topic_Empty()
+ {
+ MqttTopicValidator.ThrowIfInvalidSubscribe(string.Empty);
+ }
+ }
+}