mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-17 17:53:03 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cbd5dd778 | ||
|
|
ed31c51237 | ||
|
|
e01707d25e | ||
|
|
73f118fe1c | ||
|
|
0234918f30 | ||
|
|
c90ba7e061 |
@@ -6,3 +6,5 @@
|
|||||||
* [Server] Fixed wrong client ID passed to _InterceptingUnsubscriptionEventArgs_ (#1631, thanks to @ghord).
|
* [Server] Fixed wrong client ID passed to _InterceptingUnsubscriptionEventArgs_ (#1631, thanks to @ghord).
|
||||||
* [Server] Exposed socket settings for TCP keep alive in TCP options (#1544).
|
* [Server] Exposed socket settings for TCP keep alive in TCP options (#1544).
|
||||||
* [Server] Exposed server session items at server level and allow custom session items for injected application messages (#1638).
|
* [Server] Exposed server session items at server level and allow custom session items for injected application messages (#1638).
|
||||||
|
* [Server] Improved performance for retained message handling when subscribing using "DoNotSendOnSubscribe" or "SendAtSubscribeIfNewSubscriptionOnly" (#1661, thanks to @Int32Overflow).
|
||||||
|
* [Server] Added support for changing the used TLS certificate while the server is running (#1652, thanks to @YAJeff). The certificate provider will now be invoked for every new connection!
|
||||||
|
|||||||
+18
-1
@@ -4,7 +4,13 @@
|
|||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
Console.WriteLine("Welcome to MQTTnet samples!");
|
Console.BackgroundColor = ConsoleColor.White;
|
||||||
|
Console.BackgroundColor = ConsoleColor.Red;
|
||||||
|
Console.Title = "Samples - MQTTnet";
|
||||||
|
Console.WriteLine("----------------------------------------------");
|
||||||
|
Console.WriteLine("- Welcome to MQTTnet samples! -");
|
||||||
|
Console.WriteLine("----------------------------------------------");
|
||||||
|
Console.ResetColor();
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList();
|
var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList();
|
||||||
@@ -16,7 +22,11 @@ foreach (var sampleClass in sampleClasses)
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
Console.Write("Please choose sample class (press Enter to continue): ");
|
Console.Write("Please choose sample class (press Enter to continue): ");
|
||||||
|
Console.ResetColor();
|
||||||
|
|
||||||
var input = Console.ReadLine();
|
var input = Console.ReadLine();
|
||||||
var selectedIndex = int.Parse(input ?? "0");
|
var selectedIndex = int.Parse(input ?? "0");
|
||||||
var selectedSampleClass = sampleClasses[selectedIndex];
|
var selectedSampleClass = sampleClasses[selectedIndex];
|
||||||
@@ -29,12 +39,19 @@ foreach (var sampleMethod in sampleMethods)
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
Console.Write("Please choose sample (press Enter to continue): ");
|
Console.Write("Please choose sample (press Enter to continue): ");
|
||||||
|
Console.ResetColor();
|
||||||
|
|
||||||
input = Console.ReadLine();
|
input = Console.ReadLine();
|
||||||
selectedIndex = int.Parse(input ?? "0");
|
selectedIndex = int.Parse(input ?? "0");
|
||||||
var selectedSampleMethod = sampleMethods[selectedIndex];
|
var selectedSampleMethod = sampleMethods[selectedIndex];
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
Console.WriteLine("Executing sample...");
|
Console.WriteLine("Executing sample...");
|
||||||
|
Console.ResetColor();
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ using MQTTnet.Server;
|
|||||||
|
|
||||||
namespace MQTTnet.Samples.Server;
|
namespace MQTTnet.Samples.Server;
|
||||||
|
|
||||||
public static class Server_Asp_Net_Samples
|
public static class Server_ASP_NET_Samples
|
||||||
{
|
{
|
||||||
public static Task Start_Server_With_WebSockets_Support()
|
public static Task Start_Server_With_WebSockets_Support()
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
// ReSharper disable UnusedType.Global
|
||||||
|
// ReSharper disable UnusedMember.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
|
namespace MQTTnet.Samples.Server;
|
||||||
|
|
||||||
|
public static class Server_TLS_Samples
|
||||||
|
{
|
||||||
|
public static async Task Run_Server_With_Self_Signed_Certificate()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This sample starts a simple MQTT server which will accept any TCP connection.
|
||||||
|
* It also has an encrypted connection using a self signed TLS certificate.
|
||||||
|
*
|
||||||
|
* See sample "Run_Minimal_Server" for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var mqttFactory = new MqttFactory();
|
||||||
|
|
||||||
|
// This certificate is self signed so that
|
||||||
|
var certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||||
|
|
||||||
|
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().Build();
|
||||||
|
|
||||||
|
using (var mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions))
|
||||||
|
{
|
||||||
|
await mqttServer.StartAsync();
|
||||||
|
|
||||||
|
Console.WriteLine("Press Enter to exit.");
|
||||||
|
Console.ReadLine();
|
||||||
|
|
||||||
|
// Stop and dispose the MQTT server if it is no longer needed!
|
||||||
|
await mqttServer.StopAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static X509Certificate2 CreateSelfSignedCertificate(string oid)
|
||||||
|
{
|
||||||
|
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||||
|
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||||
|
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||||
|
sanBuilder.AddDnsName("localhost");
|
||||||
|
|
||||||
|
using (var rsa = RSA.Create())
|
||||||
|
{
|
||||||
|
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||||
|
|
||||||
|
certRequest.CertificateExtensions.Add(
|
||||||
|
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||||
|
|
||||||
|
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new(oid) }, false));
|
||||||
|
|
||||||
|
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||||
|
|
||||||
|
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
||||||
|
{
|
||||||
|
var pfxCertificate = new X509Certificate2(
|
||||||
|
certificate.Export(X509ContentType.Pfx),
|
||||||
|
(string)null!,
|
||||||
|
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||||
|
|
||||||
|
return pfxCertificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
#if !WINDOWS_UWP && (NET48_OR_GREATER || NET5_0_OR_GREATER)
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Authentication;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Certificates;
|
||||||
|
using MQTTnet.Client;
|
||||||
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
using MQTTnet.Tests.Mockups;
|
||||||
|
|
||||||
|
namespace MQTTnet.Tests.Server
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public sealed class Tls_Tests : BaseTestClass
|
||||||
|
{
|
||||||
|
static X509Certificate2 CreateCertificate(string oid)
|
||||||
|
{
|
||||||
|
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||||
|
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||||
|
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||||
|
sanBuilder.AddDnsName("localhost");
|
||||||
|
|
||||||
|
using (var rsa = RSA.Create())
|
||||||
|
{
|
||||||
|
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||||
|
|
||||||
|
certRequest.CertificateExtensions.Add(
|
||||||
|
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||||
|
|
||||||
|
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(oid) }, false));
|
||||||
|
|
||||||
|
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||||
|
|
||||||
|
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
||||||
|
{
|
||||||
|
var pfxCertificate = new X509Certificate2(
|
||||||
|
certificate.Export(X509ContentType.Pfx),
|
||||||
|
(string)null,
|
||||||
|
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||||
|
|
||||||
|
return pfxCertificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Tls_Swap_Test()
|
||||||
|
{
|
||||||
|
var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500);
|
||||||
|
var serverOptionsBuilder = testEnvironment.Factory.CreateServerOptionsBuilder();
|
||||||
|
|
||||||
|
var firstOid = "1.3.6.1.5.5.7.3.1";
|
||||||
|
var secondOid = "1.3.6.1.5.5.7.3.2";
|
||||||
|
|
||||||
|
var certificateProvider = new CertificateProvider
|
||||||
|
{
|
||||||
|
CurrentCertificate = CreateCertificate(firstOid)
|
||||||
|
};
|
||||||
|
|
||||||
|
serverOptionsBuilder.WithoutDefaultEndpoint().WithEncryptedEndpoint().WithEncryptionSslProtocol(SslProtocols.Tls12).WithEncryptionCertificate(certificateProvider);
|
||||||
|
|
||||||
|
var serverOptions = serverOptionsBuilder.Build();
|
||||||
|
|
||||||
|
var server = testEnvironment.CreateServer(serverOptions);
|
||||||
|
|
||||||
|
var publishedCount = 0;
|
||||||
|
server.InterceptingPublishAsync += args =>
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref publishedCount);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
await server.StartAsync();
|
||||||
|
|
||||||
|
var firstClient = await ConnectClientAsync(
|
||||||
|
testEnvironment,
|
||||||
|
args =>
|
||||||
|
{
|
||||||
|
Assert.AreEqual(firstOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
var firstClientReceivedCount = 0;
|
||||||
|
firstClient.ApplicationMessageReceivedAsync += args =>
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref firstClientReceivedCount);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
await firstClient.SubscribeAsync("TestTopic1");
|
||||||
|
|
||||||
|
await firstClient.PublishAsync(
|
||||||
|
new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = "TestTopic1",
|
||||||
|
Payload = new byte[] { 1, 2, 3, 4 }
|
||||||
|
});
|
||||||
|
|
||||||
|
await testEnvironment.Server.InjectApplicationMessage(
|
||||||
|
new InjectedMqttApplicationMessage(
|
||||||
|
new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = "TestTopic1",
|
||||||
|
Payload = new byte[] { 1, 2, 3, 4 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
certificateProvider.CurrentCertificate = CreateCertificate(secondOid);
|
||||||
|
|
||||||
|
// Validate that the certificate was switched
|
||||||
|
var secondClient = await ConnectClientAsync(
|
||||||
|
testEnvironment,
|
||||||
|
args =>
|
||||||
|
{
|
||||||
|
Assert.AreEqual(secondOid, ((X509Certificate2)args.Certificate).Extensions.OfType<X509EnhancedKeyUsageExtension>().First().EnhancedKeyUsages[0].Value);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
var secondClientReceivedCount = 0;
|
||||||
|
secondClient.ApplicationMessageReceivedAsync += args =>
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref secondClientReceivedCount);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
await secondClient.SubscribeAsync("TestTopic2");
|
||||||
|
|
||||||
|
await firstClient.PublishAsync(
|
||||||
|
new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = "TestTopic2",
|
||||||
|
Payload = new byte[] { 1, 2, 3, 4 }
|
||||||
|
});
|
||||||
|
|
||||||
|
await testEnvironment.Server.InjectApplicationMessage(
|
||||||
|
new InjectedMqttApplicationMessage(
|
||||||
|
new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = "TestTopic2",
|
||||||
|
Payload = new byte[] { 1, 2, 3, 4 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Ensure first client still works
|
||||||
|
await firstClient.PublishAsync(
|
||||||
|
new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = "TestTopic1",
|
||||||
|
Payload = new byte[] { 1, 2, 3, 4 }
|
||||||
|
});
|
||||||
|
|
||||||
|
await testEnvironment.Server.InjectApplicationMessage(
|
||||||
|
new InjectedMqttApplicationMessage(
|
||||||
|
new MqttApplicationMessage
|
||||||
|
{
|
||||||
|
Topic = "TestTopic1",
|
||||||
|
Payload = new byte[] { 1, 2, 3, 4 }
|
||||||
|
}));
|
||||||
|
|
||||||
|
await Task.Delay(1000);
|
||||||
|
|
||||||
|
Assert.AreEqual(6, publishedCount);
|
||||||
|
Assert.AreEqual(4, firstClientReceivedCount);
|
||||||
|
Assert.AreEqual(2, secondClientReceivedCount);
|
||||||
|
|
||||||
|
await server.StopAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task<IMqttClient> ConnectClientAsync(TestEnvironment testEnvironment, Func<MqttClientCertificateValidationEventArgs, bool> certValidator)
|
||||||
|
{
|
||||||
|
var clientOptionsBuilder = testEnvironment.Factory.CreateClientOptionsBuilder();
|
||||||
|
clientOptionsBuilder.WithClientId(Guid.NewGuid().ToString())
|
||||||
|
.WithTcpServer("localhost", 8883)
|
||||||
|
.WithTls(
|
||||||
|
tls =>
|
||||||
|
{
|
||||||
|
tls.UseTls = true;
|
||||||
|
tls.SslProtocol = SslProtocols.Tls12;
|
||||||
|
tls.CertificateValidationHandler = certValidator;
|
||||||
|
});
|
||||||
|
|
||||||
|
var clientOptions = clientOptionsBuilder.Build();
|
||||||
|
return await testEnvironment.ConnectClient(clientOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class CertificateProvider : ICertificateProvider
|
||||||
|
{
|
||||||
|
public X509Certificate2 CurrentCertificate { get; set; }
|
||||||
|
|
||||||
|
public X509Certificate2 GetCertificate()
|
||||||
|
{
|
||||||
|
return CurrentCertificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,19 +1,17 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
#if !WINDOWS_UWP
|
#if !WINDOWS_UWP
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MQTTnet.Adapter;
|
||||||
|
using MQTTnet.Diagnostics;
|
||||||
using MQTTnet.Internal;
|
using MQTTnet.Internal;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
namespace MQTTnet.Implementations
|
||||||
{
|
{
|
||||||
@@ -21,16 +19,25 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
|
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
|
||||||
|
|
||||||
MqttServerOptions _serverOptions;
|
|
||||||
CancellationTokenSource _cancellationTokenSource;
|
CancellationTokenSource _cancellationTokenSource;
|
||||||
|
|
||||||
|
MqttServerOptions _serverOptions;
|
||||||
|
|
||||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||||
|
|
||||||
public bool TreatSocketOpeningErrorAsWarning { get; set; }
|
public bool TreatSocketOpeningErrorAsWarning { get; set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
if (_cancellationTokenSource != null) throw new InvalidOperationException("Server is already started.");
|
if (_cancellationTokenSource != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Server is already started.");
|
||||||
|
}
|
||||||
|
|
||||||
_serverOptions = options;
|
_serverOptions = options;
|
||||||
|
|
||||||
@@ -38,7 +45,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
if (options.DefaultEndpointOptions.IsEnabled)
|
if (options.DefaultEndpointOptions.IsEnabled)
|
||||||
{
|
{
|
||||||
RegisterListeners(options.DefaultEndpointOptions, null, logger, _cancellationTokenSource.Token);
|
RegisterListeners(options.DefaultEndpointOptions, logger, _cancellationTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.TlsEndpointOptions?.IsEnabled == true)
|
if (options.TlsEndpointOptions?.IsEnabled == true)
|
||||||
@@ -48,13 +55,7 @@ namespace MQTTnet.Implementations
|
|||||||
throw new ArgumentException("TLS certificate is not set.");
|
throw new ArgumentException("TLS certificate is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var tlsCertificate = options.TlsEndpointOptions.CertificateProvider.GetCertificate();
|
RegisterListeners(options.TlsEndpointOptions, logger, _cancellationTokenSource.Token);
|
||||||
if (!tlsCertificate.HasPrivateKey)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterListeners(options.TlsEndpointOptions, tlsCertificate, logger, _cancellationTokenSource.Token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
@@ -66,11 +67,6 @@ namespace MQTTnet.Implementations
|
|||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cleanup()
|
void Cleanup()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -91,11 +87,22 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterListeners(MqttServerTcpEndpointBaseOptions tcpEndpointOptions, X509Certificate2 tlsCertificate, IMqttNetLogger logger, CancellationToken cancellationToken)
|
Task OnClientAcceptedAsync(IMqttChannelAdapter channelAdapter)
|
||||||
|
{
|
||||||
|
var clientHandler = ClientHandler;
|
||||||
|
if (clientHandler == null)
|
||||||
|
{
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientHandler(channelAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterListeners(MqttServerTcpEndpointBaseOptions tcpEndpointOptions, IMqttNetLogger logger, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!tcpEndpointOptions.BoundInterNetworkAddress.Equals(IPAddress.None))
|
if (!tcpEndpointOptions.BoundInterNetworkAddress.Equals(IPAddress.None))
|
||||||
{
|
{
|
||||||
var listenerV4 = new MqttTcpServerListener(AddressFamily.InterNetwork, _serverOptions, tcpEndpointOptions, tlsCertificate, logger)
|
var listenerV4 = new MqttTcpServerListener(AddressFamily.InterNetwork, _serverOptions, tcpEndpointOptions, logger)
|
||||||
{
|
{
|
||||||
ClientHandler = OnClientAcceptedAsync
|
ClientHandler = OnClientAcceptedAsync
|
||||||
};
|
};
|
||||||
@@ -108,7 +115,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
if (!tcpEndpointOptions.BoundInterNetworkV6Address.Equals(IPAddress.None))
|
if (!tcpEndpointOptions.BoundInterNetworkV6Address.Equals(IPAddress.None))
|
||||||
{
|
{
|
||||||
var listenerV6 = new MqttTcpServerListener(AddressFamily.InterNetworkV6, _serverOptions, tcpEndpointOptions, tlsCertificate, logger)
|
var listenerV6 = new MqttTcpServerListener(AddressFamily.InterNetworkV6, _serverOptions, tcpEndpointOptions, logger)
|
||||||
{
|
{
|
||||||
ClientHandler = OnClientAcceptedAsync
|
ClientHandler = OnClientAcceptedAsync
|
||||||
};
|
};
|
||||||
@@ -119,17 +126,6 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task OnClientAcceptedAsync(IMqttChannelAdapter channelAdapter)
|
|
||||||
{
|
|
||||||
var clientHandler = ClientHandler;
|
|
||||||
if (clientHandler == null)
|
|
||||||
{
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientHandler(channelAdapter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -27,7 +27,6 @@ namespace MQTTnet.Implementations
|
|||||||
readonly MqttServerOptions _serverOptions;
|
readonly MqttServerOptions _serverOptions;
|
||||||
readonly MqttServerTcpEndpointBaseOptions _options;
|
readonly MqttServerTcpEndpointBaseOptions _options;
|
||||||
readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
|
readonly MqttServerTlsTcpEndpointOptions _tlsOptions;
|
||||||
readonly X509Certificate2 _tlsCertificate;
|
|
||||||
|
|
||||||
CrossPlatformSocket _socket;
|
CrossPlatformSocket _socket;
|
||||||
IPEndPoint _localEndPoint;
|
IPEndPoint _localEndPoint;
|
||||||
@@ -36,13 +35,11 @@ namespace MQTTnet.Implementations
|
|||||||
AddressFamily addressFamily,
|
AddressFamily addressFamily,
|
||||||
MqttServerOptions serverOptions,
|
MqttServerOptions serverOptions,
|
||||||
MqttServerTcpEndpointBaseOptions tcpEndpointOptions,
|
MqttServerTcpEndpointBaseOptions tcpEndpointOptions,
|
||||||
X509Certificate2 tlsCertificate,
|
|
||||||
IMqttNetLogger logger)
|
IMqttNetLogger logger)
|
||||||
{
|
{
|
||||||
_addressFamily = addressFamily;
|
_addressFamily = addressFamily;
|
||||||
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
_serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions));
|
||||||
_options = tcpEndpointOptions ?? throw new ArgumentNullException(nameof(tcpEndpointOptions));
|
_options = tcpEndpointOptions ?? throw new ArgumentNullException(nameof(tcpEndpointOptions));
|
||||||
_tlsCertificate = tlsCertificate;
|
|
||||||
_rootLogger = logger;
|
_rootLogger = logger;
|
||||||
_logger = logger.WithSource(nameof(MqttTcpServerListener));
|
_logger = logger.WithSource(nameof(MqttTcpServerListener));
|
||||||
|
|
||||||
@@ -66,7 +63,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
_localEndPoint = new IPEndPoint(boundIp, _options.Port);
|
_localEndPoint = new IPEndPoint(boundIp, _options.Port);
|
||||||
|
|
||||||
_logger.Info("Starting TCP listener (Endpoint='{0}', TLS={1}).", _localEndPoint, _tlsCertificate != null);
|
_logger.Info("Starting TCP listener (Endpoint={0}, TLS={1})", _localEndPoint, _tlsOptions?.CertificateProvider != null);
|
||||||
|
|
||||||
_socket = new CrossPlatformSocket(_addressFamily);
|
_socket = new CrossPlatformSocket(_addressFamily);
|
||||||
|
|
||||||
@@ -115,7 +112,7 @@ namespace MQTTnet.Implementations
|
|||||||
|
|
||||||
_socket.Listen(_options.ConnectionBacklog);
|
_socket.Listen(_options.ConnectionBacklog);
|
||||||
|
|
||||||
_logger.Verbose("TCP listener started (Endpoint='{0}'.", _localEndPoint);
|
_logger.Verbose("TCP listener started (Endpoint={0})", _localEndPoint);
|
||||||
|
|
||||||
Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
|
Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).RunInBackground(_logger);
|
||||||
|
|
||||||
@@ -128,7 +125,7 @@ namespace MQTTnet.Implementations
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Warning(exception, "Error while creating listener socket for local end point '{0}'.", _localEndPoint);
|
_logger.Warning(exception, "Error while starting TCP listener (Endpoint={0})", _localEndPoint);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,10 +133,6 @@ namespace MQTTnet.Implementations
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_socket?.Dispose();
|
_socket?.Dispose();
|
||||||
|
|
||||||
#if !NET452
|
|
||||||
_tlsCertificate?.Dispose();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken)
|
async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken)
|
||||||
@@ -170,7 +163,7 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Error(exception, "Error while accepting connection at TCP listener {0} TLS={1}.", _localEndPoint, _tlsCertificate != null);
|
_logger.Error(exception, "Error while accepting TCP connection (Endpoint={0})", _localEndPoint);
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,24 +178,26 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
remoteEndPoint = clientSocket.RemoteEndPoint.ToString();
|
remoteEndPoint = clientSocket.RemoteEndPoint.ToString();
|
||||||
|
|
||||||
_logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.",
|
_logger.Verbose("TCP client '{0}' accepted (Local endpoint={1})", remoteEndPoint, _localEndPoint);
|
||||||
remoteEndPoint,
|
|
||||||
_localEndPoint,
|
|
||||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
|
||||||
|
|
||||||
clientSocket.NoDelay = _options.NoDelay;
|
clientSocket.NoDelay = _options.NoDelay;
|
||||||
stream = clientSocket.GetStream();
|
stream = clientSocket.GetStream();
|
||||||
X509Certificate2 clientCertificate = null;
|
var clientCertificate = _tlsOptions?.CertificateProvider?.GetCertificate();
|
||||||
|
|
||||||
if (_tlsCertificate != null)
|
if (clientCertificate != null)
|
||||||
{
|
{
|
||||||
|
if (!clientCertificate.HasPrivateKey)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
|
||||||
|
}
|
||||||
|
|
||||||
var sslStream = new SslStream(stream, false, _tlsOptions.RemoteCertificateValidationCallback);
|
var sslStream = new SslStream(stream, false, _tlsOptions.RemoteCertificateValidationCallback);
|
||||||
|
|
||||||
#if NETCOREAPP3_1_OR_GREATER
|
#if NETCOREAPP3_1_OR_GREATER
|
||||||
await sslStream.AuthenticateAsServerAsync(
|
await sslStream.AuthenticateAsServerAsync(
|
||||||
new SslServerAuthenticationOptions()
|
new SslServerAuthenticationOptions
|
||||||
{
|
{
|
||||||
ServerCertificate = _tlsCertificate,
|
ServerCertificate = clientCertificate,
|
||||||
ClientCertificateRequired = _tlsOptions.ClientCertificateRequired,
|
ClientCertificateRequired = _tlsOptions.ClientCertificateRequired,
|
||||||
EnabledSslProtocols = _tlsOptions.SslProtocol,
|
EnabledSslProtocols = _tlsOptions.SslProtocol,
|
||||||
CertificateRevocationCheckMode = _tlsOptions.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
|
CertificateRevocationCheckMode = _tlsOptions.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
|
||||||
@@ -211,7 +206,7 @@ namespace MQTTnet.Implementations
|
|||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
#else
|
#else
|
||||||
await sslStream.AuthenticateAsServerAsync(
|
await sslStream.AuthenticateAsServerAsync(
|
||||||
_tlsCertificate,
|
clientCertificate,
|
||||||
_tlsOptions.ClientCertificateRequired,
|
_tlsOptions.ClientCertificateRequired,
|
||||||
_tlsOptions.SslProtocol,
|
_tlsOptions.SslProtocol,
|
||||||
_tlsOptions.CheckCertificateRevocation).ConfigureAwait(false);
|
_tlsOptions.CheckCertificateRevocation).ConfigureAwait(false);
|
||||||
@@ -254,7 +249,7 @@ namespace MQTTnet.Implementations
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Error(exception, "Error while handling client connection.");
|
_logger.Error(exception, "Error while handling TCP client connection");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -269,10 +264,7 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Verbose("Client '{0}' disconnected at TCP listener '{1}, {2}'.",
|
_logger.Verbose("TCP client '{0}' disconnected (Local endpoint={1})", remoteEndPoint, _localEndPoint);
|
||||||
remoteEndPoint,
|
|
||||||
_localEndPoint,
|
|
||||||
_addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace MQTTnet.Internal
|
|||||||
|
|
||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3 && !WINDOWS_UWP
|
||||||
if (typeof(IDisposable).IsAssignableFrom(typeof(TItem)))
|
if (typeof(IDisposable).IsAssignableFrom(typeof(TItem)))
|
||||||
{
|
{
|
||||||
while (_queue.TryDequeue(out TItem item))
|
while (_queue.TryDequeue(out TItem item))
|
||||||
|
|||||||
@@ -403,6 +403,18 @@ namespace MQTTnet.Server
|
|||||||
CreateSubscriptionResult createSubscriptionResult,
|
CreateSubscriptionResult createSubscriptionResult,
|
||||||
SubscribeResult subscribeResult)
|
SubscribeResult subscribeResult)
|
||||||
{
|
{
|
||||||
|
if (createSubscriptionResult.Subscription.RetainHandling == MqttRetainHandling.DoNotSendOnSubscribe)
|
||||||
|
{
|
||||||
|
// This is a MQTT V5+ feature.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createSubscriptionResult.Subscription.RetainHandling == MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly && !createSubscriptionResult.IsNewSubscription)
|
||||||
|
{
|
||||||
|
// This is a MQTT V5+ feature.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (var index = retainedMessages.Count - 1; index >= 0; index--)
|
for (var index = retainedMessages.Count - 1; index >= 0; index--)
|
||||||
{
|
{
|
||||||
var retainedMessage = retainedMessages[index];
|
var retainedMessage = retainedMessages[index];
|
||||||
@@ -411,18 +423,6 @@ namespace MQTTnet.Server
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (createSubscriptionResult.Subscription.RetainHandling == MqttRetainHandling.DoNotSendOnSubscribe)
|
|
||||||
{
|
|
||||||
// This is a MQTT V5+ feature.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (createSubscriptionResult.Subscription.RetainHandling == MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly && !createSubscriptionResult.IsNewSubscription)
|
|
||||||
{
|
|
||||||
// This is a MQTT V5+ feature.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MqttTopicFilterComparer.Compare(retainedMessage.Topic, createSubscriptionResult.Subscription.Topic) != MqttTopicFilterCompareResult.IsMatch)
|
if (MqttTopicFilterComparer.Compare(retainedMessage.Topic, createSubscriptionResult.Subscription.Topic) != MqttTopicFilterCompareResult.IsMatch)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -198,6 +198,18 @@ namespace MQTTnet.Server
|
|||||||
_options.TlsEndpointOptions.CertificateProvider = new X509CertificateProvider(certificate);
|
_options.TlsEndpointOptions.CertificateProvider = new X509CertificateProvider(certificate);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MqttServerOptionsBuilder WithEncryptionCertificate(ICertificateProvider certificateProvider)
|
||||||
|
{
|
||||||
|
if (certificateProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(certificateProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
_options.TlsEndpointOptions.CertificateProvider = certificateProvider;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user