Compare commits

...
Author SHA1 Message Date
Christian 9cbd5dd778 Update ReleaseNotes.md 2023-01-28 13:08:24 +01:00
Christian ed31c51237 Code cleanup 2023-01-28 13:08:21 +01:00
Christian e01707d25e Improve samples 2023-01-28 13:08:08 +01:00
Jeffery MichaelandJeff Scherrer 73f118fe1c Moved the usage of certificate provider into the server listener (#1652)
* Moved the usage of certificate provider into the server listener so that each connection has the posibility of receiving a new SSL cert.

* Removed warning from unused field.

* Reduced changeds on original codebase.

---------

Co-authored-by: Jeff Scherrer <Jeff.Scherrer@vw.com>
2023-01-28 12:18:31 +01:00
Int32Overflow 0234918f30 Performance impact fix (#1661) 2023-01-28 12:12:20 +01:00
Jeffery Michael c90ba7e061 Fixed broken UWP build (#1655)
Added compiler declaritive to check for WINDOWS_UWP on IsAssignableFrom call.
2023-01-28 12:02:22 +01:00
10 changed files with 376 additions and 78 deletions
+2
View File
@@ -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
View File
@@ -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()
{ {
+75
View File
@@ -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;
}
}
}
}
+204
View File
@@ -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,44 +1,51 @@
// 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
{ {
public sealed class MqttTcpServerAdapter : IMqttServerAdapter public sealed class MqttTcpServerAdapter : IMqttServerAdapter
{ {
readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>(); readonly List<MqttTcpServerListener> _listeners = new List<MqttTcpServerListener>();
CancellationTokenSource _cancellationTokenSource;
MqttServerOptions _serverOptions; MqttServerOptions _serverOptions;
CancellationTokenSource _cancellationTokenSource;
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;
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
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,8 +27,7 @@ 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");
} }
} }
} }
+1 -1
View File
@@ -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
} }
} }