diff --git a/.github/workflows/ReleaseNotes.md b/.github/workflows/ReleaseNotes.md index 0aafba3b..fae4c230 100644 --- a/.github/workflows/ReleaseNotes.md +++ b/.github/workflows/ReleaseNotes.md @@ -1,4 +1,8 @@ * [Core] Add validation of maximum string lengths (#1718). +* [Core] Added .NET 4.8 builds (#1729). +* [Core] Exposed more details of DISCONNECT packet in log (#1729). * [Client] Added overloads for setting packet payload and will payload (#1720). -* [Client] The proper connect result is now exposed in the _Disconnected_ event when authentication fails (#1139). +* [Client] The proper connect result is now exposed in the _Disconnected_ event when authentication fails (#1139). +* [Client] Exposed more TLS options (#1729). +* [Client] Fixed wrong return code conversion (#1729). * [Server] Improved performance by changing internal locking strategy for subscriptions (#1716, thanks to @zeheng). diff --git a/Source/MQTTnet.Extensions.ManagedClient/MQTTnet.Extensions.ManagedClient.csproj b/Source/MQTTnet.Extensions.ManagedClient/MQTTnet.Extensions.ManagedClient.csproj index 7723c486..bd4e14ea 100644 --- a/Source/MQTTnet.Extensions.ManagedClient/MQTTnet.Extensions.ManagedClient.csproj +++ b/Source/MQTTnet.Extensions.ManagedClient/MQTTnet.Extensions.ManagedClient.csproj @@ -2,7 +2,7 @@ netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0 - $(TargetFrameworks);net452;net461 + $(TargetFrameworks);net452;net461;net48 $(TargetFrameworks);uap10.0 MQTTnet.Extensions.ManagedClient diff --git a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj index 42e2a07c..9a21ac89 100644 --- a/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj +++ b/Source/MQTTnet.Extensions.Rpc/MQTTnet.Extensions.Rpc.csproj @@ -2,7 +2,7 @@ netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0 - $(TargetFrameworks);net452;net461 + $(TargetFrameworks);net452;net461;net48 $(TargetFrameworks);uap10.0 MQTTnet.Extensions.Rpc diff --git a/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj b/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj index 080ae1f8..059c5e10 100644 --- a/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj +++ b/Source/MQTTnet.Extensions.WebSocket4Net/MQTTnet.Extensions.WebSocket4Net.csproj @@ -2,7 +2,7 @@ netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0 - $(TargetFrameworks);net452;net461 + $(TargetFrameworks);net452;net461;net48 $(TargetFrameworks);uap10.0 MQTTnet.Extensions.WebSocket4Net diff --git a/Source/MQTTnet.Tests/Formatter/MqttPacketSerialization_V3_Tests.cs b/Source/MQTTnet.Tests/Formatter/MqttPacketSerialization_V3_Tests.cs index 53ea016d..3a99ff5f 100644 --- a/Source/MQTTnet.Tests/Formatter/MqttPacketSerialization_V3_Tests.cs +++ b/Source/MQTTnet.Tests/Formatter/MqttPacketSerialization_V3_Tests.cs @@ -78,7 +78,7 @@ namespace MQTTnet.Tests.Formatter Assert.IsNull(deserialized.UserProperties); // Not supported in v3.1.1 } - [TestMethod] + [TestMethod] public void Serialize_Full_MqttConnAckPacket_V310() { var connAckPacket = new MqttConnAckPacket diff --git a/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs b/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs index 9939d0a1..69ad9d9e 100644 --- a/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs +++ b/Source/MQTTnet/Client/Options/MqttClientTlsOptions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; @@ -30,11 +31,21 @@ namespace MQTTnet.Client #endif #if NETCOREAPP3_1_OR_GREATER - public List ApplicationProtocols { get; set; } - - public System.Net.Security.CipherSuitesPolicy CipherSuitesPolicy { get; set; } + public List ApplicationProtocols { get; set; } + + public CipherSuitesPolicy CipherSuitesPolicy { get; set; } + + public EncryptionPolicy EncryptionPolicy { get; set; } = EncryptionPolicy.RequireEncryption; + + public bool AllowRenegotiation { get; set; } = true; #endif + /// + /// Gets or sets the target host. + /// If the value is null or empty the same host as the TCP socket host will be used. + /// + public string TargetHost { get; set; } + #if NET48 || NETCOREAPP3_1_OR_GREATER public SslProtocols SslProtocol { get; set; } = SslProtocols.Tls12 | SslProtocols.Tls13; #else diff --git a/Source/MQTTnet/Diagnostics/Runtime/TargetFrameworkProvider.cs b/Source/MQTTnet/Diagnostics/Runtime/TargetFrameworkProvider.cs index fd259587..d4e76338 100644 --- a/Source/MQTTnet/Diagnostics/Runtime/TargetFrameworkProvider.cs +++ b/Source/MQTTnet/Diagnostics/Runtime/TargetFrameworkProvider.cs @@ -16,6 +16,8 @@ namespace MQTTnet.Diagnostics return "net461"; #elif NET472 return "net472"; +#elif NET48 + return "net48"; #elif NETSTANDARD1_3 return "netstandard1.3"; #elif NETSTANDARD2_0 diff --git a/Source/MQTTnet/Formatter/MqttConnectReasonCodeConverter.cs b/Source/MQTTnet/Formatter/MqttConnectReasonCodeConverter.cs index 6278a605..cad1799b 100644 --- a/Source/MQTTnet/Formatter/MqttConnectReasonCodeConverter.cs +++ b/Source/MQTTnet/Formatter/MqttConnectReasonCodeConverter.cs @@ -2,54 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using MQTTnet.Exceptions; using MQTTnet.Protocol; namespace MQTTnet.Formatter { public static class MqttConnectReasonCodeConverter { - public static MqttConnectReasonCode ToConnectReasonCode(MqttConnectReturnCode returnCode) - { - switch (returnCode) - { - case MqttConnectReturnCode.ConnectionAccepted: - { - return MqttConnectReasonCode.Success; - } - - case MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion: - { - return MqttConnectReasonCode.UnsupportedProtocolVersion; - } - - case MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword: - { - return MqttConnectReasonCode.BadUserNameOrPassword; - } - - case MqttConnectReturnCode.ConnectionRefusedIdentifierRejected: - { - return MqttConnectReasonCode.ClientIdentifierNotValid; - } - - case MqttConnectReturnCode.ConnectionRefusedServerUnavailable: - { - return MqttConnectReasonCode.ServerUnavailable; - } - - case MqttConnectReturnCode.ConnectionRefusedNotAuthorized: - { - return MqttConnectReasonCode.NotAuthorized; - } - - default: - { - throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3)."); - } - } - } - public static MqttConnectReturnCode ToConnectReturnCode(MqttConnectReasonCode reasonCode) { switch (reasonCode) @@ -59,11 +17,13 @@ namespace MQTTnet.Formatter return MqttConnectReturnCode.ConnectionAccepted; } + case MqttConnectReasonCode.Banned: case MqttConnectReasonCode.NotAuthorized: { return MqttConnectReturnCode.ConnectionRefusedNotAuthorized; } + case MqttConnectReasonCode.BadAuthenticationMethod: case MqttConnectReasonCode.BadUserNameOrPassword: { return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword; @@ -79,6 +39,7 @@ namespace MQTTnet.Formatter return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion; } + case MqttConnectReasonCode.UseAnotherServer: case MqttConnectReasonCode.ServerUnavailable: case MqttConnectReasonCode.ServerBusy: case MqttConnectReasonCode.ServerMoved: @@ -87,9 +48,7 @@ namespace MQTTnet.Formatter } default: - { - throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3)."); - } + return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion; } } } diff --git a/Source/MQTTnet/Formatter/V5/MqttV5PacketDecoder.cs b/Source/MQTTnet/Formatter/V5/MqttV5PacketDecoder.cs index 0b6627ce..6fae9245 100644 --- a/Source/MQTTnet/Formatter/V5/MqttV5PacketDecoder.cs +++ b/Source/MQTTnet/Formatter/V5/MqttV5PacketDecoder.cs @@ -130,6 +130,9 @@ namespace MQTTnet.Formatter.V5 MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce }; + // Also set the return code of MQTT 3.1.1 for backward compatibility and debugging purposes. + packet.ReturnCode = MqttConnectReasonCodeConverter.ToConnectReturnCode(packet.ReasonCode); + var propertiesReader = new MqttV5PropertiesReader(_bufferReader); while (propertiesReader.MoveNext()) { diff --git a/Source/MQTTnet/Implementations/MqttTcpChannel.cs b/Source/MQTTnet/Implementations/MqttTcpChannel.cs index 1d700384..fb1a63f8 100644 --- a/Source/MQTTnet/Implementations/MqttTcpChannel.cs +++ b/Source/MQTTnet/Implementations/MqttTcpChannel.cs @@ -100,6 +100,12 @@ namespace MQTTnet.Implementations if (_tcpOptions.TlsOptions?.UseTls == true) { + var targetHost = _tcpOptions.TlsOptions.TargetHost; + if (string.IsNullOrEmpty(targetHost)) + { + targetHost = _tcpOptions.Server; + } + var sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback); try { @@ -111,14 +117,16 @@ namespace MQTTnet.Implementations EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol, CertificateRevocationCheckMode = _tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode, - TargetHost = _tcpOptions.Server, - CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy + TargetHost = targetHost, + CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy, + EncryptionPolicy = _tcpOptions.TlsOptions.EncryptionPolicy, + AllowRenegotiation = _tcpOptions.TlsOptions.AllowRenegotiation }; await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false); #else await sslStream.AuthenticateAsClientAsync( - _tcpOptions.Server, + targetHost, LoadCertificates(), _tcpOptions.TlsOptions.SslProtocol, !_tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors) @@ -290,12 +298,12 @@ namespace MQTTnet.Implementations X509CertificateCollection LoadCertificates() { - var certificates = new X509CertificateCollection(); if (_tcpOptions.TlsOptions.Certificates == null) { - return certificates; + return null; } + var certificates = new X509CertificateCollection(); foreach (var certificate in _tcpOptions.TlsOptions.Certificates) { certificates.Add(certificate); diff --git a/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs b/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs index 4760d328..fbabd51d 100644 --- a/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs +++ b/Source/MQTTnet/Implementations/MqttWebSocketChannel.cs @@ -220,6 +220,8 @@ namespace MQTTnet.Implementations throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net452'."); #elif NET461 throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net461'."); +#elif NET48 + throw new NotSupportedException("Remote certificate validation callback is not supported when using 'net48'."); #else clientWebSocket.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { diff --git a/Source/MQTTnet/MQTTnet.csproj b/Source/MQTTnet/MQTTnet.csproj index 6cbeb03d..a339ed68 100644 --- a/Source/MQTTnet/MQTTnet.csproj +++ b/Source/MQTTnet/MQTTnet.csproj @@ -11,7 +11,7 @@ netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0 - $(TargetFrameworks);net452;net461 + $(TargetFrameworks);net452;net461;net48 $(TargetFrameworks);uap10.0 7.3 MQTTnet diff --git a/Source/MQTTnet/Packets/MqttDisconnectPacket.cs b/Source/MQTTnet/Packets/MqttDisconnectPacket.cs index ea39e111..8fe505a3 100644 --- a/Source/MQTTnet/Packets/MqttDisconnectPacket.cs +++ b/Source/MQTTnet/Packets/MqttDisconnectPacket.cs @@ -36,7 +36,7 @@ namespace MQTTnet.Packets public override string ToString() { - return $"Disconnect: [ReasonCode={ReasonCode}]"; + return $"Disconnect: [ReasonCode={ReasonCode}] [ReasonString={ReasonString}] [ServerReference={ServerReference}] [SessionExpiryInterval={SessionExpiryInterval}]"; } } } \ No newline at end of file