diff --git a/Source/MQTTnet.Server/Configuration/EndpointConfiguration.cs b/Source/MQTTnet.Server/Configuration/EndpointConfiguration.cs deleted file mode 100644 index b4b0496..0000000 --- a/Source/MQTTnet.Server/Configuration/EndpointConfiguration.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MQTTnet.Server.Configuration -{ - public class EndpointConfiguration - { - public int Port { get; set; } = 1883; - - public int BacklogSize { get; set; } = 10; - } -} diff --git a/Source/MQTTnet.Server/Configuration/ListenModel.cs b/Source/MQTTnet.Server/Configuration/ListenModel.cs index bf62a68..1715312 100644 --- a/Source/MQTTnet.Server/Configuration/ListenModel.cs +++ b/Source/MQTTnet.Server/Configuration/ListenModel.cs @@ -1,4 +1,7 @@ -namespace MQTTnet.Server.Configuration +using System.IO; +using System.Net; + +namespace MQTTnet.Server.Configuration { /// /// Listen Entry Settings Model @@ -12,19 +15,142 @@ { } + /// + /// Path to Certificate + /// + public string CertificatePath { get; set; } + + /// + /// Enabled / Disable + /// + public bool Enabled { get; set; } = true; + + /// + /// Listen Address + /// + public string IPv4 { get; set; } + /// /// Listen Address /// - public string Address { get; set; } + public string IPv6 { get; set; } /// /// Listen Port /// - public int Port { get; set; } + public int Port { get; set; } = 1883; + + /// + /// Read Certificate file + /// + /// + public byte[] ReadCertificate() + { + if (string.IsNullOrEmpty(CertificatePath) || string.IsNullOrWhiteSpace(CertificatePath)) + { + throw new FileNotFoundException("No path set"); + } + + if (!File.Exists(CertificatePath)) + { + throw new FileNotFoundException($"Could not find Certificate in path: {CertificatePath}"); + } + + return File.ReadAllBytes(CertificatePath); + } /// - /// Protocol Type + /// Read IPv4 /// - public ListenProtocolTypes Protocol { get; set; } = ListenProtocolTypes.HTTP; + /// + public IPAddress ReafIPv4() + { + if (IPv4 == "*") + { + return IPAddress.Parse("0.0.0.0"); + } + + if (IPv4 == "localhost") + { + return IPAddress.Parse("127.0.0.1"); + } + + if (IPAddress.TryParse(IPv4, out var ip)) + { + return ip; + } + + throw new System.Exception($"Could not parse IPv4 address: {IPv4}"); + } + + /// + /// Read IPv4 + /// + /// + public bool TryReadIPv4(out IPAddress address) + { + if (IPv4 == "*") + { + address = IPAddress.Parse("::"); + return true; + } + + if (IPv4 == "localhost") + { + address = IPAddress.Parse("::1"); + return true; + } + + if (IPv4 == "disable") + { + address = null; + return false; + } + + if (IPAddress.TryParse(IPv4, out var ip)) + { + address = ip; + return true; + } + else + { + throw new System.Exception($"Could not parse IPv4 address: {IPv4}"); + } + } + + /// + /// Read IPv6 + /// + /// + public bool TryReadIPv6(out IPAddress address) + { + if (IPv6 == "*") + { + address = IPAddress.Parse("::"); + return true; + } + + if (IPv6 == "localhost") + { + address = IPAddress.Parse("::1"); + return true; + } + + if (IPv6 == "disable") + { + address = null; + return false; + } + + if (IPAddress.TryParse(IPv6, out var ip)) + { + address = ip; + return true; + } + else + { + throw new System.Exception($"Could not parse IPv6 address: {IPv6}"); + } + } } } \ No newline at end of file diff --git a/Source/MQTTnet.Server/Configuration/ListenProtocolTypes.cs b/Source/MQTTnet.Server/Configuration/ListenProtocolTypes.cs deleted file mode 100644 index 84a0a48..0000000 --- a/Source/MQTTnet.Server/Configuration/ListenProtocolTypes.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace MQTTnet.Server.Configuration -{ - /// - /// Listen Protocol Types - /// - public enum ListenProtocolTypes - { - /// - /// HTTP - /// - HTTP = 0, - - /// - /// HTTPS - /// - HTTPS = 1, - - /// - /// MQTT - /// - MQTT = 20 - } -} \ No newline at end of file diff --git a/Source/MQTTnet.Server/Configuration/MqttNetServerConfiguration.cs b/Source/MQTTnet.Server/Configuration/MqttNetServerConfiguration.cs deleted file mode 100644 index 632b8d1..0000000 --- a/Source/MQTTnet.Server/Configuration/MqttNetServerConfiguration.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MQTTnet.Server.Configuration -{ - public class MqttNetServerConfiguration - { - - } -} diff --git a/Source/MQTTnet.Server/Configuration/SettingsModel.cs b/Source/MQTTnet.Server/Configuration/SettingsModel.cs index e393084..47332dc 100644 --- a/Source/MQTTnet.Server/Configuration/SettingsModel.cs +++ b/Source/MQTTnet.Server/Configuration/SettingsModel.cs @@ -1,15 +1,42 @@ -using System.Collections.Generic; - -namespace MQTTnet.Server.Configuration +namespace MQTTnet.Server.Configuration { /// /// Main Settings Model /// public class SettingsModel { + public SettingsModel() + { + } + + /// + /// Set default connection timeout in seconds + /// + public int CommunicationTimeout { get; set; } = 15; + + /// + /// Set 0 to disable connection backlogging + /// + public int ConnectionBacklog { get; set; } + + /// + /// Enable support for persistent sessions + /// + public bool EnablePersistentSessions { get; set; } = false; + /// /// Listen Settings /// - public IEnumerable Listen { get; set; } + public ListenModel Listen { get; set; } = new ListenModel(); + + /// + /// Encryption Listen Settings + /// + public ListenModel ListenEncryption { get; set; } = new ListenModel(); + + /// + /// Set limit for max pending messages per client + /// + public int MaxPendingMessagesPerClient { get; set; } = 250; } } \ No newline at end of file diff --git a/Source/MQTTnet.Server/Mqtt/MqttServerService.cs b/Source/MQTTnet.Server/Mqtt/MqttServerService.cs index 39651ed..046876d 100644 --- a/Source/MQTTnet.Server/Mqtt/MqttServerService.cs +++ b/Source/MQTTnet.Server/Mqtt/MqttServerService.cs @@ -16,18 +16,19 @@ namespace MQTTnet.Server.Mqtt { private readonly ILogger _logger; + private readonly Configuration.SettingsModel _settings; + private readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor; private readonly MqttClientConnectedHandler _mqttClientConnectedHandler; private readonly MqttClientDisconnectedHandler _mqttClientDisconnectedHandler; private readonly MqttClientSubscribedTopicHandler _mqttClientSubscribedTopicHandler; private readonly MqttClientUnsubscribedTopicHandler _mqttClientUnsubscribedTopicHandler; private readonly MqttConnectionValidator _mqttConnectionValidator; + private readonly IMqttServer _mqttServer; private readonly MqttSubscriptionInterceptor _mqttSubscriptionInterceptor; - private readonly MqttApplicationMessageInterceptor _mqttApplicationMessageInterceptor; private readonly PythonScriptHostService _pythonScriptHostService; - private readonly IMqttServer _mqttServer; - public MqttServerService( + Configuration.SettingsModel settings, CustomMqttFactory mqttFactory, MqttWebSocketServerAdapter webSocketServerAdapter, MqttClientConnectedHandler mqttClientConnectedHandler, @@ -40,6 +41,7 @@ namespace MQTTnet.Server.Mqtt PythonScriptHostService pythonScriptHostService, ILogger logger) { + _settings = settings; _mqttClientConnectedHandler = mqttClientConnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientConnectedHandler)); _mqttClientDisconnectedHandler = mqttClientDisconnectedHandler ?? throw new ArgumentNullException(nameof(mqttClientDisconnectedHandler)); _mqttClientSubscribedTopicHandler = mqttClientSubscribedTopicHandler ?? throw new ArgumentNullException(nameof(mqttClientSubscribedTopicHandler)); @@ -64,19 +66,75 @@ namespace MQTTnet.Server.Mqtt _pythonScriptHostService.RegisterProxyObject("publish", new Action(Publish)); var options = new MqttServerOptionsBuilder() - .WithDefaultEndpoint() - .WithDefaultEndpointPort(1883) + .WithMaxPendingMessagesPerClient(_settings.MaxPendingMessagesPerClient) + .WithDefaultCommunicationTimeout(TimeSpan.FromSeconds(_settings.CommunicationTimeout)) .WithConnectionValidator(_mqttConnectionValidator) .WithApplicationMessageInterceptor(_mqttApplicationMessageInterceptor) - .WithSubscriptionInterceptor(_mqttSubscriptionInterceptor) - .Build(); + .WithSubscriptionInterceptor(_mqttSubscriptionInterceptor); + + // Configure unencrypted connections + if (_settings.Listen.Enabled) + { + options.WithDefaultEndpoint(); + if (_settings.Listen.TryReadIPv6(out var address4)) + { + options.WithDefaultEndpointBoundIPAddress(address4); + } + + if (_settings.Listen.TryReadIPv6(out var address6)) + { + options.WithDefaultEndpointBoundIPV6Address(address6); + } + + if (_settings.Listen.Port > 0) + { + options.WithDefaultEndpointPort(_settings.Listen.Port); + } + } + else + { + options.WithoutDefaultEndpoint(); + } + + // Configure encrypted connections + if (_settings.ListenEncryption.Enabled) + { + options + .WithEncryptedEndpoint() + .WithEncryptionSslProtocol(System.Security.Authentication.SslProtocols.Tls12) + .WithEncryptionCertificate(_settings.ListenEncryption.ReadCertificate()); + + if (_settings.ListenEncryption.TryReadIPv6(out var address4)) + { + options.WithDefaultEndpointBoundIPAddress(address4); + } + + if (_settings.ListenEncryption.TryReadIPv6(out var address6)) + { + options.WithDefaultEndpointBoundIPV6Address(address6); + } + + if (_settings.Listen.Port > 0) + { + options.WithEncryptedEndpointPort(_settings.ListenEncryption.Port); + } + } + else + { + options.WithoutEncryptedEndpoint(); + } + + if (_settings.ConnectionBacklog > 0) + { + options.WithConnectionBacklog(_settings.ConnectionBacklog); + } _mqttServer.ClientConnectedHandler = _mqttClientConnectedHandler; _mqttServer.ClientDisconnectedHandler = _mqttClientDisconnectedHandler; _mqttServer.ClientSubscribedTopicHandler = _mqttClientSubscribedTopicHandler; _mqttServer.ClientUnsubscribedTopicHandler = _mqttClientUnsubscribedTopicHandler; - _mqttServer.StartAsync(options).GetAwaiter().GetResult(); + _mqttServer.StartAsync(options.Build()).GetAwaiter().GetResult(); _logger.LogInformation("MQTT server started."); } @@ -127,4 +185,4 @@ namespace MQTTnet.Server.Mqtt } } } -} +} \ No newline at end of file diff --git a/Source/MQTTnet.Server/Startup.cs b/Source/MQTTnet.Server/Startup.cs index 14763d2..c4eb45a 100644 --- a/Source/MQTTnet.Server/Startup.cs +++ b/Source/MQTTnet.Server/Startup.cs @@ -15,11 +15,11 @@ namespace MQTTnet.Server { public Startup(IConfiguration configuration) { - //var builder = new ConfigurationBuilder() - // .AddJsonFile("appsettings.json") - // .AddEnvironmentVariables(); + var builder = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .AddEnvironmentVariables(); - //Configuration = builder.Build(); + Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; } @@ -52,6 +52,12 @@ namespace MQTTnet.Server { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + // Read settings + var settings = new Configuration.SettingsModel(); + Configuration.Bind("MQTTnet", settings); + services.AddSingleton(settings); + + // Wire up dependencies services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/Source/MQTTnet.Server/appsettings.json b/Source/MQTTnet.Server/appsettings.json index 0bf2d9e..f5e5a5e 100644 --- a/Source/MQTTnet.Server/appsettings.json +++ b/Source/MQTTnet.Server/appsettings.json @@ -1,32 +1,33 @@ { - "MQTTnetServer": { - /* - Wildcard Addresses: - * - All local IP addresses - localhost - Localhost only - - Supported Protocols: - 0 - HTTP - 1 - HTTPS - 20 - MQTT - */ - "Listen": [ - { - "Address": "localhost", - "Port": 5000, - "Protocol": 0 - }, - { - "Address": "*", - "Port": 1883, - "Protocol": 20 - } - ] - }, - "Logging": { - "LogLevel": { - "Default": "Warning" - } - }, - "AllowedHosts": "*" + "MQTTnet": { + /* + Wildcard Addresses: + * - All local IP addresses + localhost - Localhost only + disable - Skip address assignment + */ + "Listen": { + "Enabled": true, + "IPv4": "localhost", + "IPv6": "localhost", + "Port": 1883 + }, + "ListenEncryption": { + "Enabled": false, + "IPv4": "localhost", + "IPv6": "localhost", + "Port": 8883, + "CertificatePath": "/absolute/path/to/certificate" + }, + "CommunicationTimeout": 15, /* In seconds */ + "ConnectionBacklog": 0, /* Set 0 to disable */ + "EnablePersistentSessions": false, + "MaxPendingMessagesPerClient": 250 + }, + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" }