mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-17 17:53:03 +08:00
Expose more properties to connection validator context.
This commit is contained in:
@@ -5,6 +5,8 @@ using MQTTnet.Client.Publishing;
|
||||
using MQTTnet.Client.Subscribing;
|
||||
using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Server;
|
||||
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
|
||||
|
||||
namespace MQTTnet.Formatter
|
||||
{
|
||||
@@ -20,6 +22,8 @@ namespace MQTTnet.Formatter
|
||||
|
||||
MqttConnectPacket CreateConnectPacket(MqttApplicationMessage willApplicationMessage, IMqttClientOptions options);
|
||||
|
||||
MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext);
|
||||
|
||||
MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket);
|
||||
|
||||
MqttClientUnsubscribeResult CreateClientUnsubscribeResult(MqttUnsubscribePacket unsubscribePacket, MqttUnsubAckPacket unsubAckPacket);
|
||||
|
||||
@@ -9,6 +9,8 @@ using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
|
||||
|
||||
namespace MQTTnet.Formatter.V3
|
||||
{
|
||||
@@ -124,6 +126,16 @@ namespace MQTTnet.Formatter.V3
|
||||
};
|
||||
}
|
||||
|
||||
public MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext)
|
||||
{
|
||||
if (connectionValidatorContext == null) throw new ArgumentNullException(nameof(connectionValidatorContext));
|
||||
|
||||
return new MqttConnAckPacket
|
||||
{
|
||||
ReturnCode = new MqttConnectReasonCodeConverter().ToConnectReturnCode(connectionValidatorContext.ReasonCode)
|
||||
};
|
||||
}
|
||||
|
||||
public MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
||||
{
|
||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||
|
||||
@@ -10,6 +10,9 @@ using MQTTnet.Client.Unsubscribing;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
using MqttClientSubscribeResult = MQTTnet.Client.Subscribing.MqttClientSubscribeResult;
|
||||
|
||||
namespace MQTTnet.Formatter.V5
|
||||
{
|
||||
@@ -129,6 +132,22 @@ namespace MQTTnet.Formatter.V5
|
||||
};
|
||||
}
|
||||
|
||||
public MqttConnAckPacket CreateConnAckPacket(MqttConnectionValidatorContext connectionValidatorContext)
|
||||
{
|
||||
return new MqttConnAckPacket
|
||||
{
|
||||
ReasonCode = connectionValidatorContext.ReasonCode,
|
||||
Properties = new MqttConnAckPacketProperties
|
||||
{
|
||||
UserProperties = connectionValidatorContext.UserProperties,
|
||||
AuthenticationMethod = connectionValidatorContext.AuthenticationMethod,
|
||||
AuthenticationData = connectionValidatorContext.ResponseAuthenticationData,
|
||||
AssignedClientIdentifier = connectionValidatorContext.AssignedClientIdentifier,
|
||||
ReasonString = connectionValidatorContext.ReasonString
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public MqttClientSubscribeResult CreateClientSubscribeResult(MqttSubscribePacket subscribePacket, MqttSubAckPacket subAckPacket)
|
||||
{
|
||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Protocol
|
||||
{
|
||||
public class MqttConnectReasonCodeConverter
|
||||
{
|
||||
public MqttConnectReturnCode ToConnectReturnCode(MqttConnectReasonCode reasonCode)
|
||||
{
|
||||
switch (reasonCode)
|
||||
{
|
||||
case MqttConnectReasonCode.Success:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionAccepted;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.NotAuthorized:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedNotAuthorized;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.BadUserNameOrPassword:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.ClientIdentifierNotValid:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.UnsupportedProtocolVersion:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedUnacceptableProtocolVersion;
|
||||
}
|
||||
|
||||
case MqttConnectReasonCode.ServerUnavailable:
|
||||
case MqttConnectReasonCode.ServerBusy:
|
||||
case MqttConnectReasonCode.ServerMoved:
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedServerUnavailable;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new MqttProtocolViolationException("Unable to convert connect reason code (MQTTv5) to return code (MQTTv3).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public 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).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
@@ -227,7 +228,7 @@ namespace MQTTnet.Server
|
||||
private async Task HandleClientAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
|
||||
{
|
||||
var disconnectType = MqttClientDisconnectType.NotClean;
|
||||
var clientId = string.Empty;
|
||||
string clientId = null;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -242,20 +243,12 @@ namespace MQTTnet.Server
|
||||
|
||||
var validatorContext = await ValidateConnectionAsync(connectPacket, channelAdapter).ConfigureAwait(false);
|
||||
|
||||
if (validatorContext.ReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||
if (validatorContext.ReasonCode != MqttConnectReasonCode.Success)
|
||||
{
|
||||
// TODO: Move to channel adapter data converter.
|
||||
|
||||
// Send failure response here without preparing a session. The result for a successful connect
|
||||
// will be sent from the session itself.
|
||||
await channelAdapter.SendPacketAsync(
|
||||
new MqttConnAckPacket
|
||||
{
|
||||
ReturnCode = validatorContext.ReturnCode,
|
||||
ReasonCode = MqttConnectReasonCode.NotAuthorized
|
||||
},
|
||||
_options.DefaultCommunicationTimeout,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
var connAckPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(validatorContext);
|
||||
await channelAdapter.SendPacketAsync(connAckPacket, _options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -275,39 +268,51 @@ namespace MQTTnet.Server
|
||||
}
|
||||
finally
|
||||
{
|
||||
_connections.TryRemove(clientId, out _);
|
||||
|
||||
if (!_options.EnablePersistentSessions)
|
||||
if (clientId != null)
|
||||
{
|
||||
await DeleteSessionAsync(clientId).ConfigureAwait(false);
|
||||
_connections.TryRemove(clientId, out _);
|
||||
|
||||
if (!_options.EnablePersistentSessions)
|
||||
{
|
||||
await DeleteSessionAsync(clientId).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await TryCleanupChannelAsync(channelAdapter).ConfigureAwait(false);
|
||||
|
||||
await _eventDispatcher.HandleClientDisconnectedAsync(clientId, disconnectType).ConfigureAwait(false);
|
||||
if (clientId != null)
|
||||
{
|
||||
await _eventDispatcher.TryHandleClientDisconnectedAsync(clientId, disconnectType).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<MqttConnectionValidatorContext> ValidateConnectionAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter)
|
||||
private async Task<MqttConnectionValidatorContext> ValidateConnectionAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter)
|
||||
{
|
||||
var context = new MqttConnectionValidatorContext(
|
||||
connectPacket.ClientId,
|
||||
connectPacket.Username,
|
||||
connectPacket.Password,
|
||||
connectPacket.WillMessage,
|
||||
clientAdapter.Endpoint,
|
||||
clientAdapter.IsSecureConnection,
|
||||
clientAdapter.ClientCertificate);
|
||||
var context = new MqttConnectionValidatorContext(connectPacket, channelAdapter);
|
||||
|
||||
var connectionValidator = _options.ConnectionValidator;
|
||||
|
||||
if (connectionValidator == null)
|
||||
{
|
||||
context.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
|
||||
context.ReasonCode = MqttConnectReasonCode.Success;
|
||||
return context;
|
||||
}
|
||||
|
||||
await connectionValidator.ValidateConnectionAsync(context).ConfigureAwait(false);
|
||||
|
||||
// Check the client ID and set a random one if supported.
|
||||
if (string.IsNullOrEmpty(connectPacket.ClientId) &&
|
||||
channelAdapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500)
|
||||
{
|
||||
connectPacket.ClientId = context.AssignedClientIdentifier;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(connectPacket.ClientId))
|
||||
{
|
||||
context.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,45 +1,90 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public class MqttConnectionValidatorContext
|
||||
{
|
||||
public MqttConnectionValidatorContext(
|
||||
string clientId,
|
||||
string username,
|
||||
byte[] password,
|
||||
MqttApplicationMessage willMessage,
|
||||
string endpoint,
|
||||
bool isSecureConnection,
|
||||
X509Certificate2 clientCertificate)
|
||||
private readonly MqttConnectPacket _connectPacket;
|
||||
private readonly IMqttChannelAdapter _clientAdapter;
|
||||
|
||||
public MqttConnectionValidatorContext(MqttConnectPacket connectPacket, IMqttChannelAdapter clientAdapter)
|
||||
{
|
||||
ClientId = clientId;
|
||||
Username = username;
|
||||
RawPassword = password;
|
||||
WillMessage = willMessage;
|
||||
Endpoint = endpoint;
|
||||
IsSecureConnection = isSecureConnection;
|
||||
ClientCertificate = clientCertificate;
|
||||
_connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket));
|
||||
_clientAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter));
|
||||
}
|
||||
|
||||
public string ClientId { get; }
|
||||
public string ClientId => _connectPacket.ClientId;
|
||||
|
||||
public string Username { get; }
|
||||
public string Username => _connectPacket.Username;
|
||||
|
||||
public byte[] RawPassword => _connectPacket.Password;
|
||||
|
||||
public string Password => Encoding.UTF8.GetString(RawPassword ?? new byte[0]);
|
||||
|
||||
public byte[] RawPassword { get; }
|
||||
public MqttApplicationMessage WillMessage => _connectPacket.WillMessage;
|
||||
|
||||
public MqttApplicationMessage WillMessage { get; }
|
||||
public bool CleanSession => _connectPacket.CleanSession;
|
||||
|
||||
public string Endpoint { get; }
|
||||
public ushort KeepAlivePeriod => _connectPacket.KeepAlivePeriod;
|
||||
|
||||
public bool IsSecureConnection { get; }
|
||||
public List<MqttUserProperty> UserProperties => _connectPacket.Properties?.UserProperties;
|
||||
|
||||
public X509Certificate2 ClientCertificate { get; }
|
||||
public byte[] AuthenticationData => _connectPacket.Properties?.AuthenticationData;
|
||||
|
||||
public MqttConnectReturnCode ReturnCode { get; set; } = MqttConnectReturnCode.ConnectionAccepted;
|
||||
public string AuthenticationMethod => _connectPacket.Properties?.AuthenticationMethod;
|
||||
|
||||
public uint? MaximumPacketSize => _connectPacket.Properties?.MaximumPacketSize;
|
||||
|
||||
public ushort? ReceiveMaximum => _connectPacket.Properties?.ReceiveMaximum;
|
||||
|
||||
public ushort? TopicAliasMaximum => _connectPacket.Properties?.TopicAliasMaximum;
|
||||
|
||||
public bool? RequestProblemInformation => _connectPacket.Properties?.RequestProblemInformation;
|
||||
|
||||
public bool? RequestResponseInformation => _connectPacket.Properties?.RequestResponseInformation;
|
||||
|
||||
public uint? SessionExpiryInterval => _connectPacket.Properties?.SessionExpiryInterval;
|
||||
|
||||
public uint? WillDelayInterval => _connectPacket.Properties?.WillDelayInterval;
|
||||
|
||||
public string Endpoint => _clientAdapter.Endpoint;
|
||||
|
||||
public bool IsSecureConnection => _clientAdapter.IsSecureConnection;
|
||||
|
||||
public X509Certificate2 ClientCertificate => _clientAdapter.ClientCertificate;
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion => _clientAdapter.PacketFormatterAdapter.ProtocolVersion;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for MQTTv3 only.
|
||||
/// </summary>
|
||||
[Obsolete("Use ReasonCode instead. It is MQTTv5 only but will be converted to a valid ReturnCode.")]
|
||||
public MqttConnectReturnCode ReturnCode
|
||||
{
|
||||
get => new MqttConnectReasonCodeConverter().ToConnectReturnCode(ReasonCode);
|
||||
set => ReasonCode = new MqttConnectReasonCodeConverter().ToConnectReasonCode(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used for MQTTv5 only. When a MQTTv3 client connects the enum value must be one which is
|
||||
/// also supported in MQTTv3. Otherwise the connection attempt will fail because not all codes can be
|
||||
/// converted properly.
|
||||
/// </summary>
|
||||
public MqttConnectReasonCode ReasonCode { get; set; } = MqttConnectReasonCode.Success;
|
||||
|
||||
public List<MqttUserProperty> ResponseUserProperties { get; set; }
|
||||
|
||||
public byte[] ResponseAuthenticationData { get; set; }
|
||||
|
||||
public string AssignedClientIdentifier { get; set; }
|
||||
|
||||
public string ReasonString { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
public class MqttServer : IMqttServer
|
||||
{
|
||||
private readonly MqttServerEventDispatcher _eventDispatcher = new MqttServerEventDispatcher();
|
||||
private readonly MqttServerEventDispatcher _eventDispatcher;
|
||||
private readonly ICollection<IMqttServerAdapter> _adapters;
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace MQTTnet.Server
|
||||
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
_logger = logger.CreateChildLogger(nameof(MqttServer));
|
||||
|
||||
_eventDispatcher = new MqttServerEventDispatcher(logger.CreateChildLogger(nameof(MqttServerEventDispatcher)));
|
||||
}
|
||||
|
||||
public IMqttServerStartedHandler StartedHandler { get; set; }
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Client.Receiving;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public class MqttServerEventDispatcher
|
||||
{
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
|
||||
public MqttServerEventDispatcher(IMqttNetChildLogger logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public IMqttServerClientConnectedHandler ClientConnectedHandler { get; set; }
|
||||
|
||||
public IMqttServerClientDisconnectedHandler ClientDisconnectedHandler { get; set; }
|
||||
@@ -26,15 +35,22 @@ namespace MQTTnet.Server
|
||||
return handler.HandleClientConnectedAsync(new MqttServerClientConnectedEventArgs(clientId));
|
||||
}
|
||||
|
||||
public Task HandleClientDisconnectedAsync(string clientId, MqttClientDisconnectType disconnectType)
|
||||
public async Task TryHandleClientDisconnectedAsync(string clientId, MqttClientDisconnectType disconnectType)
|
||||
{
|
||||
var handler = ClientDisconnectedHandler;
|
||||
if (handler == null)
|
||||
try
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
var handler = ClientDisconnectedHandler;
|
||||
if (handler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return handler.HandleClientDisconnectedAsync(new MqttServerClientDisconnectedEventArgs(clientId, disconnectType));
|
||||
await handler.HandleClientDisconnectedAsync(new MqttServerClientDisconnectedEventArgs(clientId, disconnectType)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error(exception, "Error while handling 'ClientDisconnected' event.");
|
||||
}
|
||||
}
|
||||
|
||||
public Task HandleClientSubscribedTopicAsync(string clientId, TopicFilter topicFilter)
|
||||
|
||||
Reference in New Issue
Block a user