mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Expose local endpoint in channel adapter (#2213)
* Expose local endpoint * Update release notes
This commit is contained in:
@@ -61,27 +61,6 @@ public sealed class MqttConnectionContext : IMqttChannelAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EndPoint RemoteEndPoint
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
// mqtt over tcp
|
|
||||||
if (_connection.RemoteEndPoint != null)
|
|
||||||
{
|
|
||||||
return _connection.RemoteEndPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mqtt over websocket
|
|
||||||
var httpFeature = _connection.Features.Get<IHttpConnectionFeature>();
|
|
||||||
if (httpFeature?.RemoteIpAddress != null)
|
|
||||||
{
|
|
||||||
return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSecureConnection
|
public bool IsSecureConnection
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -104,8 +83,50 @@ public sealed class MqttConnectionContext : IMqttChannelAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EndPoint LocalEndPoint
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// mqtt over tcp
|
||||||
|
if (_connection.LocalEndPoint != null)
|
||||||
|
{
|
||||||
|
return _connection.LocalEndPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mqtt over websocket
|
||||||
|
var httpFeature = _connection.Features.Get<IHttpConnectionFeature>();
|
||||||
|
if (httpFeature?.LocalIpAddress != null)
|
||||||
|
{
|
||||||
|
return new IPEndPoint(httpFeature.LocalIpAddress, httpFeature.LocalPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|
||||||
|
public EndPoint RemoteEndPoint
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// mqtt over tcp
|
||||||
|
if (_connection.RemoteEndPoint != null)
|
||||||
|
{
|
||||||
|
return _connection.RemoteEndPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mqtt over websocket
|
||||||
|
var httpFeature = _connection.Features.Get<IHttpConnectionFeature>();
|
||||||
|
if (httpFeature?.RemoteIpAddress != null)
|
||||||
|
{
|
||||||
|
return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_connection is SocketConnection tcp && !tcp.IsConnected)
|
if (_connection is SocketConnection tcp && !tcp.IsConnected)
|
||||||
@@ -232,8 +253,8 @@ public sealed class MqttConnectionContext : IMqttChannelAdapter
|
|||||||
var span = output.GetSpan(buffer.Length);
|
var span = output.GetSpan(buffer.Length);
|
||||||
|
|
||||||
buffer.Packet.AsSpan().CopyTo(span);
|
buffer.Packet.AsSpan().CopyTo(span);
|
||||||
int offset = buffer.Packet.Count;
|
var offset = buffer.Packet.Count;
|
||||||
buffer.Payload.CopyTo(destination: span.Slice(offset));
|
buffer.Payload.CopyTo(span.Slice(offset));
|
||||||
output.Advance(buffer.Length);
|
output.Advance(buffer.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +79,8 @@ public class SerializerBenchmark : BaseBenchmark
|
|||||||
|
|
||||||
public EndPoint RemoteEndPoint { get; set; }
|
public EndPoint RemoteEndPoint { get; set; }
|
||||||
|
|
||||||
|
public EndPoint LocalEndPoint { get; set; }
|
||||||
|
|
||||||
public bool IsSecureConnection { get; } = false;
|
public bool IsSecureConnection { get; } = false;
|
||||||
|
|
||||||
public X509Certificate2 ClientCertificate { get; set; }
|
public X509Certificate2 ClientCertificate { get; set; }
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ public sealed class MqttTcpServerListener : IDisposable
|
|||||||
var clientHandler = ClientHandler;
|
var clientHandler = ClientHandler;
|
||||||
if (clientHandler != null)
|
if (clientHandler != null)
|
||||||
{
|
{
|
||||||
var tcpChannel = new MqttTcpChannel(stream, remoteEndPoint, clientCertificate);
|
var tcpChannel = new MqttTcpChannel(stream, _localEndPoint, remoteEndPoint, clientCertificate);
|
||||||
var bufferWriter = new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax);
|
var bufferWriter = new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax);
|
||||||
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
var packetFormatterAdapter = new MqttPacketFormatterAdapter(bufferWriter);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
<NuGetAudit>true</NuGetAudit>
|
<NuGetAudit>true</NuGetAudit>
|
||||||
<NuGetAuditLevel>low</NuGetAuditLevel>
|
<NuGetAuditLevel>low</NuGetAuditLevel>
|
||||||
<AnalysisLevel>latest-Recommended</AnalysisLevel>
|
<AnalysisLevel>latest-Recommended</AnalysisLevel>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public sealed class MemoryMqttChannel : IMqttChannel
|
|||||||
|
|
||||||
public EndPoint RemoteEndPoint { get; set; }
|
public EndPoint RemoteEndPoint { get; set; }
|
||||||
|
|
||||||
|
public EndPoint LocalEndPoint { get; set; }
|
||||||
|
|
||||||
public bool IsSecureConnection { get; } = false;
|
public bool IsSecureConnection { get; } = false;
|
||||||
|
|
||||||
public X509Certificate2 ClientCertificate { get; set; }
|
public X509Certificate2 ClientCertificate { get; set; }
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class MqttTcpChannel_Tests
|
|||||||
using var clientSocket = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
|
using var clientSocket = new CrossPlatformSocket(AddressFamily.InterNetwork, ProtocolType.Tcp);
|
||||||
await clientSocket.ConnectAsync(remoteEndPoint, CancellationToken.None);
|
await clientSocket.ConnectAsync(remoteEndPoint, CancellationToken.None);
|
||||||
|
|
||||||
var tcpChannel = new MqttTcpChannel(clientSocket.GetStream(), remoteEndPoint, null);
|
var tcpChannel = new MqttTcpChannel(clientSocket.GetStream(), new DnsEndPoint("localhost", 50000), remoteEndPoint, null);
|
||||||
|
|
||||||
await Task.Delay(100, ct.Token);
|
await Task.Delay(100, ct.Token);
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -14,12 +9,18 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using MQTTnet.Formatter;
|
||||||
|
using MQTTnet.Internal;
|
||||||
|
using MQTTnet.Packets;
|
||||||
|
using MQTTnet.Protocol;
|
||||||
|
using MQTTnet.Server;
|
||||||
|
|
||||||
namespace MQTTnet.Tests.Server;
|
namespace MQTTnet.Tests.Server;
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class General_Tests : BaseTestClass
|
public class General_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
Dictionary<string, bool> _connected;
|
Dictionary<string, bool> _connected;
|
||||||
|
|
||||||
@@ -83,23 +84,53 @@ public sealed class General_Tests : BaseTestClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Deny_Connection()
|
public async Task Disconnect_Client_with_Reason()
|
||||||
{
|
{
|
||||||
using var testEnvironment = CreateTestEnvironment();
|
using var testEnvironment = CreateTestEnvironment();
|
||||||
testEnvironment.IgnoreClientLogErrors = true;
|
var disconnectPacketReceived = false;
|
||||||
|
|
||||||
var server = await testEnvironment.StartServer();
|
string testClientId = null;
|
||||||
|
|
||||||
server.ValidatingConnectionAsync += e =>
|
await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
testEnvironment.Server.ClientConnectedAsync += e =>
|
||||||
{
|
{
|
||||||
e.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
testClientId = e.ClientId;
|
||||||
return CompletedTask.Instance;
|
return CompletedTask.Instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
var client = testEnvironment.CreateClient();
|
||||||
var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptions());
|
|
||||||
|
|
||||||
Assert.AreEqual(MqttClientConnectResultCode.NotAuthorized, response.ResultCode);
|
client.InspectPacketAsync += e =>
|
||||||
|
{
|
||||||
|
if (e.Buffer.Length > 0)
|
||||||
|
{
|
||||||
|
if (e.Buffer[0] == (byte)MqttControlPacketType.Disconnect << 4)
|
||||||
|
{
|
||||||
|
disconnectPacketReceived = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
await client.ConnectAsync(new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort).WithProtocolVersion(MqttProtocolVersion.V311).Build());
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// Test client should be connected now
|
||||||
|
|
||||||
|
Assert.IsNotNull(testClientId);
|
||||||
|
|
||||||
|
// Have the server disconnect the client with SessionTakenOver reason; no DISCONNECT should be sent to MQTT 3.1.1 clients
|
||||||
|
|
||||||
|
await testEnvironment.Server.DisconnectClientAsync(testClientId, MqttDisconnectReasonCode.SessionTakenOver);
|
||||||
|
|
||||||
|
await LongTestDelay();
|
||||||
|
|
||||||
|
// MQTT 3.1.1 client should not receive a disconnect packet
|
||||||
|
|
||||||
|
Assert.IsFalse(disconnectPacketReceived, "Disconnect packet received when none is expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -152,6 +183,27 @@ public sealed class General_Tests : BaseTestClass
|
|||||||
Assert.AreEqual("y", buffer.ToString());
|
Assert.AreEqual("y", buffer.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Expose_Local_And_Remote_Endpoint()
|
||||||
|
{
|
||||||
|
using var testEnvironment = CreateTestEnvironment();
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
ValidatingConnectionEventArgs eventArgs = null;
|
||||||
|
server.ValidatingConnectionAsync += e =>
|
||||||
|
{
|
||||||
|
eventArgs = e;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
await testEnvironment.ConnectClient();
|
||||||
|
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
|
Assert.AreEqual("0.0.0.0:" + testEnvironment.ServerPort, eventArgs.ChannelAdapter.LocalEndPoint.ToString());
|
||||||
|
Assert.StartsWith("127.0.0.1:", eventArgs.ChannelAdapter.RemoteEndPoint.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Handle_Clean_Disconnect()
|
public async Task Handle_Clean_Disconnect()
|
||||||
{
|
{
|
||||||
@@ -205,35 +257,34 @@ public sealed class General_Tests : BaseTestClass
|
|||||||
var testEnvironment2 = testEnvironment;
|
var testEnvironment2 = testEnvironment;
|
||||||
|
|
||||||
tasks.Add(
|
tasks.Add(
|
||||||
Task.Run(
|
Task.Run(async () =>
|
||||||
async () =>
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
using var client = await testEnvironment2.ConnectClient();
|
||||||
{
|
// Clear retained message.
|
||||||
using var client = await testEnvironment2.ConnectClient();
|
await client.PublishAsync(
|
||||||
// Clear retained message.
|
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
||||||
await client.PublishAsync(
|
.WithPayload(EmptyBuffer.Array)
|
||||||
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
.WithRetainFlag()
|
||||||
.WithPayload(EmptyBuffer.Array)
|
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
.WithRetainFlag()
|
.Build());
|
||||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
|
||||||
.Build());
|
|
||||||
|
|
||||||
// Set retained message.
|
// Set retained message.
|
||||||
await client.PublishAsync(
|
await client.PublishAsync(
|
||||||
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
new MqttApplicationMessageBuilder().WithTopic("r" + i2)
|
||||||
.WithPayload("value")
|
.WithPayload("value")
|
||||||
.WithRetainFlag()
|
.WithRetainFlag()
|
||||||
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
|
||||||
.Build());
|
.Build());
|
||||||
|
|
||||||
await client.DisconnectAsync();
|
await client.DisconnectAsync();
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
testEnvironment2.TrackException(exception);
|
testEnvironment2.TrackException(exception);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
}
|
}
|
||||||
@@ -432,21 +483,21 @@ public sealed class General_Tests : BaseTestClass
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Publish_At_Least_Once_0x01()
|
public Task Publish_At_Least_Once_0x01()
|
||||||
{
|
{
|
||||||
await TestPublishAsync("A/B/C", MqttQualityOfServiceLevel.AtLeastOnce, "A/B/C", MqttQualityOfServiceLevel.AtLeastOnce, 1);
|
return TestPublishAsync("A/B/C", MqttQualityOfServiceLevel.AtLeastOnce, "A/B/C", MqttQualityOfServiceLevel.AtLeastOnce, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Publish_At_Most_Once_0x00()
|
public Task Publish_At_Most_Once_0x00()
|
||||||
{
|
{
|
||||||
await TestPublishAsync("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "A/B/C", MqttQualityOfServiceLevel.AtMostOnce, 1);
|
return TestPublishAsync("A/B/C", MqttQualityOfServiceLevel.AtMostOnce, "A/B/C", MqttQualityOfServiceLevel.AtMostOnce, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Publish_Exactly_Once_0x02()
|
public Task Publish_Exactly_Once_0x02()
|
||||||
{
|
{
|
||||||
await TestPublishAsync("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, 1);
|
return TestPublishAsync("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, "A/B/C", MqttQualityOfServiceLevel.ExactlyOnce, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -886,59 +937,6 @@ public sealed class General_Tests : BaseTestClass
|
|||||||
Assert.IsTrue(client.IsConnected);
|
Assert.IsTrue(client.IsConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public async Task Disconnect_Client_with_Reason()
|
|
||||||
{
|
|
||||||
using var testEnvironment = CreateTestEnvironment();
|
|
||||||
var disconnectPacketReceived = false;
|
|
||||||
|
|
||||||
string testClientId = null;
|
|
||||||
|
|
||||||
await testEnvironment.StartServer();
|
|
||||||
|
|
||||||
testEnvironment.Server.ClientConnectedAsync += e =>
|
|
||||||
{
|
|
||||||
testClientId = e.ClientId;
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
var client = testEnvironment.CreateClient();
|
|
||||||
|
|
||||||
client.InspectPacketAsync += e =>
|
|
||||||
{
|
|
||||||
if (e.Buffer.Length > 0)
|
|
||||||
{
|
|
||||||
if (e.Buffer[0] == (byte)MqttControlPacketType.Disconnect << 4)
|
|
||||||
{
|
|
||||||
disconnectPacketReceived = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return CompletedTask.Instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
await client.ConnectAsync(
|
|
||||||
new MqttClientOptionsBuilder().WithTcpServer("localhost", testEnvironment.ServerPort)
|
|
||||||
.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
|
|
||||||
.Build()
|
|
||||||
);
|
|
||||||
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
// Test client should be connected now
|
|
||||||
|
|
||||||
Assert.IsNotNull(testClientId);
|
|
||||||
|
|
||||||
// Have the server disconnect the client with SessionTakenOver reason; no DISCONNECT should be sent to MQTT 3.1.1 clients
|
|
||||||
|
|
||||||
await testEnvironment.Server.DisconnectClientAsync(testClientId, MqttDisconnectReasonCode.SessionTakenOver);
|
|
||||||
|
|
||||||
await LongTestDelay();
|
|
||||||
|
|
||||||
// MQTT 3.1.1 client should not receive a disconnect packet
|
|
||||||
|
|
||||||
Assert.IsFalse(disconnectPacketReceived, "Disconnect packet received when none is expected");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionValidationHandler(ValidatingConnectionEventArgs eventArgs)
|
void ConnectionValidationHandler(ValidatingConnectionEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!_connected.TryAdd(eventArgs.ClientId, true))
|
if (!_connected.TryAdd(eventArgs.ClientId, true))
|
||||||
|
|||||||
@@ -14,8 +14,28 @@ namespace MQTTnet.Tests.Server;
|
|||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public sealed class Security_Tests : BaseTestClass
|
public class Security_Tests : BaseTestClass
|
||||||
{
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task Deny_Connection()
|
||||||
|
{
|
||||||
|
using var testEnvironment = CreateTestEnvironment();
|
||||||
|
testEnvironment.IgnoreClientLogErrors = true;
|
||||||
|
|
||||||
|
var server = await testEnvironment.StartServer();
|
||||||
|
|
||||||
|
server.ValidatingConnectionAsync += e =>
|
||||||
|
{
|
||||||
|
e.ReasonCode = MqttConnectReasonCode.NotAuthorized;
|
||||||
|
return CompletedTask.Instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
var client = testEnvironment.CreateClient();
|
||||||
|
var response = await client.ConnectAsync(testEnvironment.CreateDefaultClientOptions());
|
||||||
|
|
||||||
|
Assert.AreEqual(MqttClientConnectResultCode.NotAuthorized, response.ResultCode);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task Do_Not_Affect_Authorized_Clients()
|
public async Task Do_Not_Affect_Authorized_Clients()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public interface IMqttChannelAdapter : IDisposable
|
|||||||
|
|
||||||
EndPoint RemoteEndPoint { get; }
|
EndPoint RemoteEndPoint { get; }
|
||||||
|
|
||||||
|
EndPoint LocalEndPoint { get; }
|
||||||
|
|
||||||
bool IsSecureConnection { get; }
|
bool IsSecureConnection { get; }
|
||||||
|
|
||||||
MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public sealed class MqttChannelAdapter : Disposable, IMqttChannelAdapter
|
|||||||
|
|
||||||
public EndPoint RemoteEndPoint => _channel.RemoteEndPoint;
|
public EndPoint RemoteEndPoint => _channel.RemoteEndPoint;
|
||||||
|
|
||||||
|
public EndPoint LocalEndPoint => _channel.LocalEndPoint;
|
||||||
|
|
||||||
public bool IsSecureConnection => _channel.IsSecureConnection;
|
public bool IsSecureConnection => _channel.IsSecureConnection;
|
||||||
|
|
||||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||||
|
|||||||
@@ -14,8 +14,11 @@ namespace MQTTnet.Channel;
|
|||||||
public interface IMqttChannel : IDisposable
|
public interface IMqttChannel : IDisposable
|
||||||
{
|
{
|
||||||
X509Certificate2 ClientCertificate { get; }
|
X509Certificate2 ClientCertificate { get; }
|
||||||
|
|
||||||
EndPoint RemoteEndPoint { get; }
|
EndPoint RemoteEndPoint { get; }
|
||||||
|
|
||||||
|
EndPoint LocalEndPoint { get; }
|
||||||
|
|
||||||
bool IsSecureConnection { get; }
|
bool IsSecureConnection { get; }
|
||||||
|
|
||||||
Task ConnectAsync(CancellationToken cancellationToken);
|
Task ConnectAsync(CancellationToken cancellationToken);
|
||||||
|
|||||||
@@ -37,10 +37,11 @@ public sealed class MqttTcpChannel : IMqttChannel
|
|||||||
IsSecureConnection = clientOptions.ChannelOptions?.TlsOptions?.UseTls == true;
|
IsSecureConnection = clientOptions.ChannelOptions?.TlsOptions?.UseTls == true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MqttTcpChannel(Stream stream, EndPoint remoteEndPoint, X509Certificate2 clientCertificate) : this()
|
public MqttTcpChannel(Stream stream, EndPoint localEndPoint, EndPoint remoteEndPoint, X509Certificate2 clientCertificate) : this()
|
||||||
{
|
{
|
||||||
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
|
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
|
||||||
|
|
||||||
|
LocalEndPoint = localEndPoint;
|
||||||
RemoteEndPoint = remoteEndPoint;
|
RemoteEndPoint = remoteEndPoint;
|
||||||
|
|
||||||
IsSecureConnection = stream is SslStream;
|
IsSecureConnection = stream is SslStream;
|
||||||
@@ -49,10 +50,13 @@ public sealed class MqttTcpChannel : IMqttChannel
|
|||||||
|
|
||||||
public X509Certificate2 ClientCertificate { get; }
|
public X509Certificate2 ClientCertificate { get; }
|
||||||
|
|
||||||
public EndPoint RemoteEndPoint { get; private set; }
|
|
||||||
|
|
||||||
public bool IsSecureConnection { get; }
|
public bool IsSecureConnection { get; }
|
||||||
|
|
||||||
|
|
||||||
|
public EndPoint LocalEndPoint { get; private set; }
|
||||||
|
|
||||||
|
public EndPoint RemoteEndPoint { get; private set; }
|
||||||
|
|
||||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CrossPlatformSocket socket = null;
|
CrossPlatformSocket socket = null;
|
||||||
@@ -87,7 +91,7 @@ public sealed class MqttTcpChannel : IMqttChannel
|
|||||||
socket.LingerState = _tcpOptions.LingerState;
|
socket.LingerState = _tcpOptions.LingerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_tcpOptions.DualMode.HasValue)
|
if (_tcpOptions.DualMode != null)
|
||||||
{
|
{
|
||||||
// It is important to avoid setting the flag if no specific value is set by the user
|
// It is important to avoid setting the flag if no specific value is set by the user
|
||||||
// because on IPv4 only networks the setter will always throw an exception. Regardless
|
// because on IPv4 only networks the setter will always throw an exception. Regardless
|
||||||
@@ -115,37 +119,20 @@ public sealed class MqttTcpChannel : IMqttChannel
|
|||||||
SslStream sslStream;
|
SslStream sslStream;
|
||||||
if (_tcpOptions.TlsOptions.CertificateSelectionHandler != null)
|
if (_tcpOptions.TlsOptions.CertificateSelectionHandler != null)
|
||||||
{
|
{
|
||||||
sslStream = new SslStream(
|
sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback, InternalUserCertificateSelectionCallback);
|
||||||
networkStream,
|
|
||||||
false,
|
|
||||||
InternalUserCertificateValidationCallback,
|
|
||||||
InternalUserCertificateSelectionCallback);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Use a different constructor depending on the options for MQTTnet so that we do not have
|
// Use a different constructor depending on the options for MQTTnet so that we do not have
|
||||||
// to copy the exact same behavior of the selection handler.
|
// to copy the exact same behavior of the selection handler.
|
||||||
sslStream = new SslStream(
|
sslStream = new SslStream(networkStream, false, InternalUserCertificateValidationCallback);
|
||||||
networkStream,
|
|
||||||
false,
|
|
||||||
InternalUserCertificateValidationCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sslOptions = new SslClientAuthenticationOptions
|
var sslOptions = CreateSslAuthenticationOptions();
|
||||||
{
|
|
||||||
ApplicationProtocols = _tcpOptions.TlsOptions.ApplicationProtocols,
|
sslOptions.TargetHost = targetHost;
|
||||||
ClientCertificates = LoadCertificates(),
|
|
||||||
EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol,
|
|
||||||
CertificateRevocationCheckMode = _tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors
|
|
||||||
? X509RevocationMode.NoCheck
|
|
||||||
: _tcpOptions.TlsOptions.RevocationMode,
|
|
||||||
TargetHost = targetHost,
|
|
||||||
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy,
|
|
||||||
EncryptionPolicy = _tcpOptions.TlsOptions.EncryptionPolicy,
|
|
||||||
AllowRenegotiation = _tcpOptions.TlsOptions.AllowRenegotiation
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_tcpOptions.TlsOptions.TrustChain?.Count > 0)
|
if (_tcpOptions.TlsOptions.TrustChain?.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -176,6 +163,7 @@ public sealed class MqttTcpChannel : IMqttChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoteEndPoint = socket.RemoteEndPoint;
|
RemoteEndPoint = socket.RemoteEndPoint;
|
||||||
|
LocalEndPoint = socket.LocalEndPoint;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -281,6 +269,21 @@ public sealed class MqttTcpChannel : IMqttChannel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SslClientAuthenticationOptions CreateSslAuthenticationOptions()
|
||||||
|
{
|
||||||
|
var sslOptions = new SslClientAuthenticationOptions
|
||||||
|
{
|
||||||
|
ApplicationProtocols = _tcpOptions.TlsOptions.ApplicationProtocols,
|
||||||
|
ClientCertificates = LoadCertificates(),
|
||||||
|
EnabledSslProtocols = _tcpOptions.TlsOptions.SslProtocol,
|
||||||
|
CertificateRevocationCheckMode = _tcpOptions.TlsOptions.IgnoreCertificateRevocationErrors ? X509RevocationMode.NoCheck : _tcpOptions.TlsOptions.RevocationMode,
|
||||||
|
CipherSuitesPolicy = _tcpOptions.TlsOptions.CipherSuitesPolicy,
|
||||||
|
EncryptionPolicy = _tcpOptions.TlsOptions.EncryptionPolicy,
|
||||||
|
AllowRenegotiation = _tcpOptions.TlsOptions.AllowRenegotiation
|
||||||
|
};
|
||||||
|
return sslOptions;
|
||||||
|
}
|
||||||
|
|
||||||
X509Certificate InternalUserCertificateSelectionCallback(
|
X509Certificate InternalUserCertificateSelectionCallback(
|
||||||
object sender,
|
object sender,
|
||||||
string targetHost,
|
string targetHost,
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ public sealed class MqttWebSocketChannel : IMqttChannel
|
|||||||
|
|
||||||
public EndPoint RemoteEndPoint { get; }
|
public EndPoint RemoteEndPoint { get; }
|
||||||
|
|
||||||
|
public EndPoint LocalEndPoint { get; }
|
||||||
|
|
||||||
public bool IsSecureConnection { get; private set; }
|
public bool IsSecureConnection { get; private set; }
|
||||||
|
|
||||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Core: Performance improvements
|
* Core: Performance improvements
|
||||||
* Server: Improved performance of retained messages when no event handler is attached (#2093, thanks to @zhaowgit)
|
* Server: Improved performance of retained messages when no event handler is attached (#2093, thanks to @zhaowgit)
|
||||||
* Server: The event `InterceptingClientEnqueue` is now also called for retained messages (BREAKING CHANGE!)
|
* Server: The event `InterceptingClientEnqueue` is now also called for retained messages (BREAKING CHANGE!)
|
||||||
|
* Server: The local end point is now also exposed in the channel adapter (#2179)
|
||||||
* Marked all projects as AOT compatible
|
* Marked all projects as AOT compatible
|
||||||
* Restored the strong name of the nugets
|
* Restored the strong name of the nugets
|
||||||
* Embedded license file in all nugets (#2197, thanks to @JensNordenbro)
|
* Embedded license file in all nugets (#2197, thanks to @JensNordenbro)
|
||||||
|
|||||||
Reference in New Issue
Block a user