@@ -0,0 +1,117 @@ | |||||
using System; | |||||
using System.Net; | |||||
using System.Net.Security; | |||||
using System.Net.Sockets; | |||||
using System.Security.Authentication; | |||||
using System.Security.Cryptography.X509Certificates; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using MQTTnet.Core.Adapter; | |||||
using MQTTnet.Core.Diagnostics; | |||||
using MQTTnet.Core.Serializer; | |||||
using MQTTnet.Core.Server; | |||||
namespace MQTTnet.Implementations | |||||
{ | |||||
public class MqttServerAdapter : IMqttServerAdapter, IDisposable | |||||
{ | |||||
private CancellationTokenSource _cancellationTokenSource; | |||||
private Socket _defaultEndpointSocket; | |||||
private Socket _tlsEndpointSocket; | |||||
private X509Certificate2 _tlsCertificate; | |||||
private bool _isRunning; | |||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected; | |||||
public void Start(MqttServerOptions options) | |||||
{ | |||||
if (_isRunning) throw new InvalidOperationException("Server is already started."); | |||||
_isRunning = true; | |||||
_cancellationTokenSource = new CancellationTokenSource(); | |||||
if (options.DefaultEndpointOptions.IsEnabled) | |||||
{ | |||||
_defaultEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); | |||||
_defaultEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetDefaultEndpointPort())); | |||||
_defaultEndpointSocket.Listen(options.ConnectionBacklog); | |||||
Task.Run(() => AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token); | |||||
} | |||||
if (options.TlsEndpointOptions.IsEnabled) | |||||
{ | |||||
if (options.TlsEndpointOptions.Certificate == null) | |||||
{ | |||||
throw new ArgumentException("TLS certificate is not set."); | |||||
} | |||||
_tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate); | |||||
_tlsEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp); | |||||
_tlsEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetTlsEndpointPort())); | |||||
_tlsEndpointSocket.Listen(options.ConnectionBacklog); | |||||
Task.Run(() => AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token); | |||||
} | |||||
} | |||||
public void Stop() | |||||
{ | |||||
_isRunning = false; | |||||
_cancellationTokenSource?.Dispose(); | |||||
_cancellationTokenSource = null; | |||||
_defaultEndpointSocket?.Dispose(); | |||||
_defaultEndpointSocket = null; | |||||
_tlsEndpointSocket?.Dispose(); | |||||
_tlsEndpointSocket = null; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
Stop(); | |||||
} | |||||
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken) | |||||
{ | |||||
while (!cancellationToken.IsCancellationRequested) | |||||
{ | |||||
try | |||||
{ | |||||
var clientSocket = await _defaultEndpointSocket.AcceptAsync(); | |||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, null), new DefaultMqttV311PacketSerializer()); | |||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter)); | |||||
} | |||||
catch (Exception exception) | |||||
{ | |||||
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while acceping connection at default endpoint."); | |||||
} | |||||
} | |||||
} | |||||
private async Task AcceptTlsEndpointConnectionsAsync(CancellationToken cancellationToken) | |||||
{ | |||||
while (!cancellationToken.IsCancellationRequested) | |||||
{ | |||||
try | |||||
{ | |||||
var clientSocket = await _defaultEndpointSocket.AcceptAsync(); | |||||
var sslStream = new SslStream(new NetworkStream(clientSocket)); | |||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false); | |||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream), new DefaultMqttV311PacketSerializer()); | |||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter)); | |||||
} | |||||
catch (Exception exception) | |||||
{ | |||||
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while acceping connection at TLS endpoint."); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,122 @@ | |||||
using System; | |||||
using System.Net.Security; | |||||
using System.Net.Sockets; | |||||
using System.Security.Authentication; | |||||
using System.Security.Cryptography.X509Certificates; | |||||
using System.Threading.Tasks; | |||||
using MQTTnet.Core.Channel; | |||||
using MQTTnet.Core.Client; | |||||
using MQTTnet.Core.Exceptions; | |||||
namespace MQTTnet.Implementations | |||||
{ | |||||
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable | |||||
{ | |||||
private readonly Socket _socket; | |||||
private SslStream _sslStream; | |||||
public MqttTcpChannel() | |||||
{ | |||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp); | |||||
} | |||||
public MqttTcpChannel(Socket socket, SslStream sslStream) | |||||
{ | |||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket)); | |||||
_sslStream = sslStream; | |||||
} | |||||
public async Task ConnectAsync(MqttClientOptions options) | |||||
{ | |||||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||||
try | |||||
{ | |||||
await _socket.ConnectAsync(options.Server, options.GetPort()); | |||||
if (options.TlsOptions.UseTls) | |||||
{ | |||||
_sslStream = new SslStream(new NetworkStream(_socket, true)); | |||||
await _sslStream.AuthenticateAsClientAsync(options.Server, LoadCertificates(options), SslProtocols.Tls12, options.TlsOptions.CheckCertificateRevocation); | |||||
} | |||||
} | |||||
catch (SocketException exception) | |||||
{ | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
} | |||||
public Task DisconnectAsync() | |||||
{ | |||||
try | |||||
{ | |||||
_sslStream.Dispose(); | |||||
_socket.Dispose(); | |||||
return Task.FromResult(0); | |||||
} | |||||
catch (SocketException exception) | |||||
{ | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
} | |||||
public Task WriteAsync(byte[] buffer) | |||||
{ | |||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer)); | |||||
try | |||||
{ | |||||
if (_sslStream != null) | |||||
{ | |||||
return _sslStream.WriteAsync(buffer, 0, buffer.Length); | |||||
} | |||||
return _socket.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None); | |||||
} | |||||
catch (SocketException exception) | |||||
{ | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
} | |||||
public Task ReadAsync(byte[] buffer) | |||||
{ | |||||
if (buffer == null) throw new ArgumentNullException(nameof(buffer)); | |||||
try | |||||
{ | |||||
if (_sslStream != null) | |||||
{ | |||||
return _sslStream.ReadAsync(buffer, 0, buffer.Length); | |||||
} | |||||
return _socket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None); | |||||
} | |||||
catch (SocketException exception) | |||||
{ | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
_socket?.Dispose(); | |||||
_sslStream?.Dispose(); | |||||
} | |||||
private static X509CertificateCollection LoadCertificates(MqttClientOptions options) | |||||
{ | |||||
var certificates = new X509CertificateCollection(); | |||||
if (options.TlsOptions.Certificates == null) | |||||
{ | |||||
return certificates; | |||||
} | |||||
foreach (var certificate in options.TlsOptions.Certificates) | |||||
{ | |||||
certificates.Add(new X509Certificate(certificate)); | |||||
} | |||||
return certificates; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard1.3</TargetFramework> | |||||
<AssemblyName>MQTTnet</AssemblyName> | |||||
<RootNamespace>MQTTnet</RootNamespace> | |||||
</PropertyGroup> | |||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" /> | |||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="System.Net.Security" Version="4.3.1" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -0,0 +1,18 @@ | |||||
using System; | |||||
using MQTTnet.Core.Adapter; | |||||
using MQTTnet.Core.Client; | |||||
using MQTTnet.Core.Serializer; | |||||
using MQTTnet.Implementations; | |||||
namespace MQTTnet | |||||
{ | |||||
public class MqttClientFactory | |||||
{ | |||||
public MqttClient CreateMqttClient(MqttClientOptions options) | |||||
{ | |||||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer())); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,18 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using MQTTnet.Core.Adapter; | |||||
using MQTTnet.Core.Server; | |||||
using MQTTnet.Implementations; | |||||
namespace MQTTnet | |||||
{ | |||||
public class MqttServerFactory | |||||
{ | |||||
public MqttServer CreateMqttServer(MqttServerOptions options) | |||||
{ | |||||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||||
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() }); | |||||
} | |||||
} | |||||
} |