diff --git a/samples/DtlsClient/Program.cs b/samples/DtlsClient/Program.cs index c383370..a69bb91 100644 --- a/samples/DtlsClient/Program.cs +++ b/samples/DtlsClient/Program.cs @@ -4,7 +4,10 @@ using System.Threading.Tasks; using CoAPNet; using CoAPNet.Dtls.Client; -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Tls; +using Org.BouncyCastle.Tls.Crypto; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; namespace CoAPDevices { @@ -16,7 +19,6 @@ static async Task Main(string[] args) var port = Coap.PortDTLS; var identity = new BasicTlsPskIdentity("user", Encoding.UTF8.GetBytes("password")); - // Create a new client using a DTLS endpoint with the remote host and Identity var client = new CoapClient(new CoapDtlsClientEndPoint(host, port, new ExamplePskDtlsClient(identity))); // Create a cancelation token that cancels after 1 minute @@ -69,26 +71,18 @@ static async Task Main(string[] args) public class ExamplePskDtlsClient : PskTlsClient { public ExamplePskDtlsClient(TlsPskIdentity pskIdentity) - : base(pskIdentity) + : base(new BcTlsCrypto(new SecureRandom()), pskIdentity) { } - public ExamplePskDtlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity) - : base(cipherFactory, pskIdentity) - { - } - - public ExamplePskDtlsClient(TlsCipherFactory cipherFactory, TlsDHVerifier dhVerifier, TlsPskIdentity pskIdentity) - : base(cipherFactory, dhVerifier, pskIdentity) + public override int GetHandshakeTimeoutMillis() { + return 30000; } - public override ProtocolVersion MinimumVersion => ProtocolVersion.DTLSv10; - public override ProtocolVersion ClientVersion => ProtocolVersion.DTLSv12; - - public override int GetHandshakeTimeoutMillis() + public override ProtocolVersion[] GetProtocolVersions() { - return 30000; + return ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10); } } } diff --git a/samples/DtlsServer/Program.cs b/samples/DtlsServer/Program.cs index 5e3cac3..50f6e51 100644 --- a/samples/DtlsServer/Program.cs +++ b/samples/DtlsServer/Program.cs @@ -7,10 +7,10 @@ using CoAPNet.Server; using System.Threading.Tasks; using CoAPNet.Dtls.Server; -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Tls; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Console; -using Microsoft.Extensions.Options; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Security; namespace CoAPDevices { @@ -82,7 +82,7 @@ public class ExamplePskDtlsServer : PskTlsServer private readonly ExamplePskIdentityManager pskIdentityManager; public ExamplePskDtlsServer(ExamplePskIdentityManager pskIdentityManager) - : base(pskIdentityManager) + : base(new BcTlsCrypto(new SecureRandom()), pskIdentityManager) { this.pskIdentityManager = pskIdentityManager; } @@ -97,8 +97,10 @@ public string GetIdentity() return this.pskIdentityManager.GetIdentity(); } - protected override ProtocolVersion MinimumVersion => ProtocolVersion.DTLSv10; - protected override ProtocolVersion MaximumVersion => ProtocolVersion.DTLSv12; + public override ProtocolVersion[] GetProtocolVersions() + { + return ProtocolVersion.DTLSv12.DownTo(ProtocolVersion.DTLSv10); + } } public class ExamplePskIdentityManager : TlsPskIdentityManager diff --git a/src/CoAPNet.Dtls/Client/CoapDtlsClientEndPoint.cs b/src/CoAPNet.Dtls/Client/CoapDtlsClientEndPoint.cs index eb1e335..337df4d 100644 --- a/src/CoAPNet.Dtls/Client/CoapDtlsClientEndPoint.cs +++ b/src/CoAPNet.Dtls/Client/CoapDtlsClientEndPoint.cs @@ -3,7 +3,7 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Tls; using Org.BouncyCastle.Security; namespace CoAPNet.Dtls.Client @@ -82,7 +82,7 @@ private void EnsureConnected() var udpClient = new UdpClient(Server, Port); - var dtlsClientProtocol = new DtlsClientProtocol(new SecureRandom()); + var dtlsClientProtocol = new DtlsClientProtocol(); _datagramTransport = dtlsClientProtocol.Connect(_tlsClient, new UdpDatagramTransport(udpClient, NetworkMtu)); _isConnected = true; } diff --git a/src/CoAPNet.Dtls/Client/UdpDatagramTransport.cs b/src/CoAPNet.Dtls/Client/UdpDatagramTransport.cs index cc5ced3..2e79de6 100644 --- a/src/CoAPNet.Dtls/Client/UdpDatagramTransport.cs +++ b/src/CoAPNet.Dtls/Client/UdpDatagramTransport.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Tls; namespace CoAPNet.Dtls.Client { diff --git a/src/CoAPNet.Dtls/CoAPNet.Dtls.csproj b/src/CoAPNet.Dtls/CoAPNet.Dtls.csproj index 24966f3..c3d5cef 100644 --- a/src/CoAPNet.Dtls/CoAPNet.Dtls.csproj +++ b/src/CoAPNet.Dtls/CoAPNet.Dtls.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/CoAPNet.Dtls/Server/CoapDtlsConnectionInformation.cs b/src/CoAPNet.Dtls/Server/CoapDtlsConnectionInformation.cs index de3edce..0423491 100644 --- a/src/CoAPNet.Dtls/Server/CoapDtlsConnectionInformation.cs +++ b/src/CoAPNet.Dtls/Server/CoapDtlsConnectionInformation.cs @@ -1,4 +1,4 @@ -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Tls; namespace CoAPNet.Dtls.Server { diff --git a/src/CoAPNet.Dtls/Server/CoapDtlsServerClientEndPoint.cs b/src/CoAPNet.Dtls/Server/CoapDtlsServerClientEndPoint.cs index 140d44d..be3ed83 100644 --- a/src/CoAPNet.Dtls/Server/CoapDtlsServerClientEndPoint.cs +++ b/src/CoAPNet.Dtls/Server/CoapDtlsServerClientEndPoint.cs @@ -1,9 +1,10 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Tls; namespace CoAPNet.Dtls.Server { @@ -29,7 +30,7 @@ public CoapDtlsServerClientEndPoint(IPEndPoint endPoint, QueueDatagramTransport public IPEndPoint EndPoint { get; } public Uri BaseUri { get; } - public string ConnectionInfo { get; private set; } + public IReadOnlyDictionary ConnectionInfo { get; private set; } public bool IsSecure => true; @@ -102,8 +103,7 @@ public void Accept(DtlsServerProtocol serverProtocol, TlsServer server) public void EnqueueDatagram(byte[] datagram) { - if (!IsClosed) - _udpTransport.ReceiveQueue.Add(datagram); + _udpTransport.EnqueueReceived(datagram); LastReceivedTime = DateTime.UtcNow; } } diff --git a/src/CoAPNet.Dtls/Server/CoapDtlsServerTransport.cs b/src/CoAPNet.Dtls/Server/CoapDtlsServerTransport.cs index faac3c3..475f4bf 100644 --- a/src/CoAPNet.Dtls/Server/CoapDtlsServerTransport.cs +++ b/src/CoAPNet.Dtls/Server/CoapDtlsServerTransport.cs @@ -8,8 +8,7 @@ using System.Threading.Tasks; using CoAPNet.Dtls.Server.Statistics; using Microsoft.Extensions.Logging; -using Org.BouncyCastle.Crypto.Tls; -using Org.BouncyCastle.Security; +using Org.BouncyCastle.Tls; namespace CoAPNet.Dtls.Server { @@ -35,9 +34,7 @@ public CoapDtlsServerTransport(CoapDtlsServerEndPoint endPoint, ICoapHandler coa _tlsServerFactory = tlsServerFactory ?? throw new ArgumentNullException(nameof(tlsServerFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - SecureRandom random = new SecureRandom(); - - _serverProtocol = new DtlsServerProtocol(random); + _serverProtocol = new DtlsServerProtocol(); _sessions = new ConcurrentDictionary(); } @@ -176,42 +173,37 @@ private async Task HandleSession(CoapDtlsServerClientEndPoint session) session.Accept(_serverProtocol, server); - if (session.ConnectionInfo != null) - { - _logger.LogInformation("New TLS connection from {EndPoint}, Server Info: {ServerInfo}", session.EndPoint, session.ConnectionInfo); - } - else + using (session.ConnectionInfo != null ? _logger.BeginScope(session.ConnectionInfo) : null) { _logger.LogInformation("New TLS connection from {EndPoint}", session.EndPoint); - } - var connectionInfo = new CoapDtlsConnectionInformation - { - LocalEndpoint = _endPoint, - RemoteEndpoint = session, - TlsServer = server - }; + var connectionInfo = new CoapDtlsConnectionInformation + { + LocalEndpoint = _endPoint, + RemoteEndpoint = session, + TlsServer = server + }; - while (!session.IsClosed && !_cts.IsCancellationRequested) - { - var packet = await session.ReceiveAsync(_cts.Token); - _logger.LogDebug("Handling CoAP Packet from {EndPoint}", session.EndPoint); - await _coapHandler.ProcessRequestAsync(connectionInfo, packet.Payload); - _logger.LogDebug("CoAP request from {EndPoint} handled!", session.EndPoint); + while (!session.IsClosed && !_cts.IsCancellationRequested) + { + var packet = await session.ReceiveAsync(_cts.Token); + _logger.LogDebug("Handling CoAP Packet from {EndPoint}", session.EndPoint); + await _coapHandler.ProcessRequestAsync(connectionInfo, packet.Payload); + _logger.LogDebug("CoAP request from {EndPoint} handled!", session.EndPoint); + } } } - catch (OperationCanceledException) + catch (Exception ex) when (IsCanceledException(ex)) { + _logger.LogDebug(ex, "Session was canceled"); } - catch (DtlsConnectionClosedException) + catch (TlsTimeoutException timeoutEx) { + _logger.LogWarning(timeoutEx, "Timeout while handling session"); } catch (TlsFatalAlert tlsAlert) { - if (!(tlsAlert.InnerException is DtlsConnectionClosedException) && tlsAlert.AlertDescription != AlertDescription.user_canceled) - { - _logger.LogWarning(tlsAlert, "TLS Error"); - } + _logger.LogWarning(tlsAlert, "TLS Error"); } catch (Exception ex) { @@ -225,6 +217,13 @@ private async Task HandleSession(CoapDtlsServerClientEndPoint session) } } + private bool IsCanceledException(Exception ex) + { + return ex is OperationCanceledException || + ex is DtlsConnectionClosedException || + (ex is TlsFatalAlert tlsAlert && (tlsAlert.InnerException is DtlsConnectionClosedException || tlsAlert.AlertDescription == AlertDescription.user_canceled)); + } + private async Task HandleCleanup() { while (!_cts.IsCancellationRequested) diff --git a/src/CoAPNet.Dtls/Server/IDtlsServerFactory.cs b/src/CoAPNet.Dtls/Server/IDtlsServerFactory.cs index 9246834..a1afe39 100644 --- a/src/CoAPNet.Dtls/Server/IDtlsServerFactory.cs +++ b/src/CoAPNet.Dtls/Server/IDtlsServerFactory.cs @@ -1,4 +1,4 @@ -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Tls; namespace CoAPNet.Dtls.Server { diff --git a/src/CoAPNet.Dtls/Server/IDtlsServerWithConnectionInfo.cs b/src/CoAPNet.Dtls/Server/IDtlsServerWithConnectionInfo.cs index a736225..41b7012 100644 --- a/src/CoAPNet.Dtls/Server/IDtlsServerWithConnectionInfo.cs +++ b/src/CoAPNet.Dtls/Server/IDtlsServerWithConnectionInfo.cs @@ -1,4 +1,5 @@ -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Tls; +using System.Collections.Generic; namespace CoAPNet.Dtls.Server { @@ -12,6 +13,6 @@ public interface IDtlsServerWithConnectionInfo : TlsServer /// Get the connection information for the connection handled by this TLS server. /// /// Information about this connection that should be logged once the connection is established. - string GetConnectionInfo(); + IReadOnlyDictionary GetConnectionInfo(); } } diff --git a/src/CoAPNet.Dtls/Server/QueueDatagramTransport.cs b/src/CoAPNet.Dtls/Server/QueueDatagramTransport.cs index 8ad7e48..9da11da 100644 --- a/src/CoAPNet.Dtls/Server/QueueDatagramTransport.cs +++ b/src/CoAPNet.Dtls/Server/QueueDatagramTransport.cs @@ -2,7 +2,7 @@ using System.Collections.Concurrent; using System.Linq; using System.Threading; -using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Tls; namespace CoAPNet.Dtls.Server { @@ -16,6 +16,7 @@ internal class QueueDatagramTransport : DatagramTransport private readonly int _sendLimit; private readonly Action _sendCallback; private readonly CancellationTokenSource _cts; + private BlockingCollection _receiveQueue; private const int MIN_IP_OVERHEAD = 20; private const int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64; @@ -27,20 +28,21 @@ public QueueDatagramTransport(int mtu, Action sendCallback) _sendLimit = mtu - MAX_IP_OVERHEAD - UDP_OVERHEAD; _sendCallback = sendCallback ?? throw new ArgumentNullException(nameof(sendCallback)); _cts = new CancellationTokenSource(); + _receiveQueue = new BlockingCollection(); } - public BlockingCollection ReceiveQueue { get; internal set; } = new BlockingCollection(); public bool IsClosed { get; private set; } public ReaderWriterLockSlim CloseLock { get; } = new ReaderWriterLockSlim(); public void Close() { + // Cancel this before locking so the lock gets released ASAP _cts.Cancel(); CloseLock.EnterWriteLock(); IsClosed = true; + _receiveQueue.Dispose(); CloseLock.ExitWriteLock(); - ReceiveQueue.Dispose(); } public int GetReceiveLimit() @@ -53,6 +55,21 @@ public int GetSendLimit() return _sendLimit; } + public void EnqueueReceived(byte[] datagram) + { + try + { + CloseLock.EnterReadLock(); + if (IsClosed) + return; + _receiveQueue.Add(datagram); + } + finally + { + CloseLock.ExitReadLock(); + } + } + public int Receive(byte[] buf, int off, int len, int waitMillis) { try @@ -61,7 +78,7 @@ public int Receive(byte[] buf, int off, int len, int waitMillis) if (IsClosed) throw new DtlsConnectionClosedException(); - var success = ReceiveQueue.TryTake(out var data, waitMillis, _cts.Token); + var success = _receiveQueue.TryTake(out var data, waitMillis, _cts.Token); if (!success) return -1; // DO NOT return 0. This will disable the wait timeout effectively for the caller and any abort logic will by bypassed! var readLen = Math.Min(len, data.Length); diff --git a/src/CoAPNet.Dtls/Server/Statistics/DtlsSessionStatistics.cs b/src/CoAPNet.Dtls/Server/Statistics/DtlsSessionStatistics.cs index 2d5dfd2..1d50858 100644 --- a/src/CoAPNet.Dtls/Server/Statistics/DtlsSessionStatistics.cs +++ b/src/CoAPNet.Dtls/Server/Statistics/DtlsSessionStatistics.cs @@ -1,11 +1,12 @@ using System; +using System.Collections.Generic; namespace CoAPNet.Dtls.Server.Statistics { public class DtlsSessionStatistics { public string EndPoint { get; set; } - public string ConnectionInfo { get; set; } + public IReadOnlyDictionary ConnectionInfo { get; set; } public DateTime SessionStartTime { get; internal set; } public DateTime LastReceivedTime { get; internal set; } }