@@ -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() }); | |||
} | |||
} | |||
} |