diff options
-rw-r--r-- | crypto/src/tls/DatagramReceiver.cs | 5 | ||||
-rw-r--r-- | crypto/src/tls/DatagramSender.cs | 5 | ||||
-rw-r--r-- | crypto/src/tls/DtlsRecordLayer.cs | 167 | ||||
-rw-r--r-- | crypto/src/tls/DtlsTransport.cs | 100 | ||||
-rw-r--r-- | crypto/src/tls/TlsUtilities.cs | 9 | ||||
-rw-r--r-- | crypto/test/src/tls/test/LoggingDatagramTransport.cs | 62 | ||||
-rw-r--r-- | crypto/test/src/tls/test/MockDatagramAssociation.cs | 59 | ||||
-rw-r--r-- | crypto/test/src/tls/test/UnreliableDatagramTransport.cs | 47 |
8 files changed, 444 insertions, 10 deletions
diff --git a/crypto/src/tls/DatagramReceiver.cs b/crypto/src/tls/DatagramReceiver.cs index 5ab605ac4..a689515f6 100644 --- a/crypto/src/tls/DatagramReceiver.cs +++ b/crypto/src/tls/DatagramReceiver.cs @@ -10,5 +10,10 @@ namespace Org.BouncyCastle.Tls /// <exception cref="IOException"/> int Receive(byte[] buf, int off, int len, int waitMillis); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <exception cref="IOException"/> + int Receive(Span<byte> buffer, int waitMillis); +#endif } } diff --git a/crypto/src/tls/DatagramSender.cs b/crypto/src/tls/DatagramSender.cs index bf14c18fe..c2a987b51 100644 --- a/crypto/src/tls/DatagramSender.cs +++ b/crypto/src/tls/DatagramSender.cs @@ -10,5 +10,10 @@ namespace Org.BouncyCastle.Tls /// <exception cref="IOException"/> void Send(byte[] buf, int off, int len); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <exception cref="IOException"/> + void Send(ReadOnlySpan<byte> buffer); +#endif } } diff --git a/crypto/src/tls/DtlsRecordLayer.cs b/crypto/src/tls/DtlsRecordLayer.cs index 7ec77c5da..bab6892b7 100644 --- a/crypto/src/tls/DtlsRecordLayer.cs +++ b/crypto/src/tls/DtlsRecordLayer.cs @@ -242,6 +242,9 @@ namespace Org.BouncyCastle.Tls /// <exception cref="IOException"/> public virtual int Receive(byte[] buf, int off, int len, int waitMillis) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Receive(buf.AsSpan(off, len), waitMillis); +#else long currentTimeMillis = DateTimeUtilities.CurrentUnixMs(); Timeout timeout = Timeout.ForWaitMillis(waitMillis, currentTimeMillis); @@ -305,11 +308,85 @@ namespace Org.BouncyCastle.Tls } return -1; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <exception cref="IOException"/> + public virtual int Receive(Span<byte> buffer, int waitMillis) + { + long currentTimeMillis = DateTimeUtilities.CurrentUnixMs(); + + Timeout timeout = Timeout.ForWaitMillis(waitMillis, currentTimeMillis); + byte[] record = null; + + while (waitMillis >= 0) + { + if (null != m_retransmitTimeout && m_retransmitTimeout.RemainingMillis(currentTimeMillis) < 1) + { + m_retransmit = null; + m_retransmitEpoch = null; + m_retransmitTimeout = null; + } + + if (Timeout.HasExpired(m_heartbeatTimeout, currentTimeMillis)) + { + if (null != m_heartbeatInFlight) + throw new TlsTimeoutException("Heartbeat timed out"); + + this.m_heartbeatInFlight = HeartbeatMessage.Create(m_context, + HeartbeatMessageType.heartbeat_request, m_heartbeat.GeneratePayload()); + this.m_heartbeatTimeout = new Timeout(m_heartbeat.TimeoutMillis, currentTimeMillis); + + this.m_heartbeatResendMillis = DtlsReliableHandshake.INITIAL_RESEND_MILLIS; + this.m_heartbeatResendTimeout = new Timeout(m_heartbeatResendMillis, currentTimeMillis); + + SendHeartbeatMessage(m_heartbeatInFlight); + } + else if (Timeout.HasExpired(m_heartbeatResendTimeout, currentTimeMillis)) + { + this.m_heartbeatResendMillis = DtlsReliableHandshake.BackOff(m_heartbeatResendMillis); + this.m_heartbeatResendTimeout = new Timeout(m_heartbeatResendMillis, currentTimeMillis); + + SendHeartbeatMessage(m_heartbeatInFlight); + } + + waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_heartbeatTimeout, currentTimeMillis); + waitMillis = Timeout.ConstrainWaitMillis(waitMillis, m_heartbeatResendTimeout, currentTimeMillis); + + // NOTE: Guard against bad logic giving a negative value + if (waitMillis < 0) + { + waitMillis = 1; + } + + int receiveLimit = System.Math.Min(buffer.Length, GetReceiveLimit()) + RECORD_HEADER_LENGTH; + if (null == record || record.Length < receiveLimit) + { + record = new byte[receiveLimit]; + } + + int received = ReceiveRecord(record, 0, receiveLimit, waitMillis); + int processed = ProcessRecord(received, record, buffer); + if (processed >= 0) + { + return processed; + } + + currentTimeMillis = DateTimeUtilities.CurrentUnixMs(); + waitMillis = Timeout.GetWaitMillis(timeout, currentTimeMillis); + } + + return -1; + } +#endif + /// <exception cref="IOException"/> public virtual void Send(byte[] buf, int off, int len) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Send(buf.AsSpan(off, len)); +#else short contentType = ContentType.application_data; if (m_inHandshake || m_writeEpoch == m_retransmitEpoch) @@ -338,7 +415,7 @@ namespace Org.BouncyCastle.Tls // Implicitly send change_cipher_spec and change to pending cipher state // TODO Send change_cipher_spec and finished records in single datagram? - byte[] data = new byte[]{ 1 }; + byte[] data = new byte[1]{ 1 }; SendRecord(ContentType.change_cipher_spec, data, 0, data.Length); this.m_writeEpoch = nextEpoch; @@ -346,7 +423,51 @@ namespace Org.BouncyCastle.Tls } SendRecord(contentType, buf, off, len); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <exception cref="IOException"/> + public virtual void Send(ReadOnlySpan<byte> buffer) + { + short contentType = ContentType.application_data; + + if (m_inHandshake || m_writeEpoch == m_retransmitEpoch) + { + contentType = ContentType.handshake; + + short handshakeType = TlsUtilities.ReadUint8(buffer); + if (handshakeType == HandshakeType.finished) + { + DtlsEpoch nextEpoch = null; + if (m_inHandshake) + { + nextEpoch = m_pendingEpoch; + } + else if (m_writeEpoch == m_retransmitEpoch) + { + nextEpoch = m_currentEpoch; + } + + if (nextEpoch == null) + { + // TODO + throw new InvalidOperationException(); + } + + // Implicitly send change_cipher_spec and change to pending cipher state + + // TODO Send change_cipher_spec and finished records in single datagram? + ReadOnlySpan<byte> data = stackalloc byte[1]{ 1 }; + SendRecord(ContentType.change_cipher_spec, data); + + this.m_writeEpoch = nextEpoch; + } + } + + SendRecord(contentType, buffer); } +#endif /// <exception cref="IOException"/> public virtual void Close() @@ -432,11 +553,13 @@ namespace Org.BouncyCastle.Tls { m_peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause); - byte[] error = new byte[2]; - error[0] = (byte)alertLevel; - error[1] = (byte)alertDescription; - +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ReadOnlySpan<byte> error = stackalloc byte[2]{ (byte)alertLevel, (byte)alertDescription }; + SendRecord(ContentType.alert, error); +#else + byte[] error = new byte[2]{ (byte)alertLevel, (byte)alertDescription }; SendRecord(ContentType.alert, error, 0, 2); +#endif } /// <exception cref="IOException"/> @@ -467,7 +590,11 @@ namespace Org.BouncyCastle.Tls // TODO Include 'currentTimeMillis' as an argument, use with Timeout, resetHeartbeat /// <exception cref="IOException"/> +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private int ProcessRecord(int received, byte[] record, Span<byte> buffer) +#else private int ProcessRecord(int received, byte[] record, byte[] buf, int off) +#endif { // NOTE: received < 0 (timeout) is covered by this first case if (received < RECORD_HEADER_LENGTH) @@ -700,7 +827,11 @@ namespace Org.BouncyCastle.Tls this.m_retransmitTimeout = null; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + decoded.buf.AsSpan(decoded.off, decoded.len).CopyTo(buffer); +#else Array.Copy(decoded.buf, decoded.off, buf, off, decoded.len); +#endif return decoded.len; } @@ -712,9 +843,7 @@ namespace Org.BouncyCastle.Tls int length = 0; if (m_recordQueue.Available >= RECORD_HEADER_LENGTH) { - byte[] lengthBytes = new byte[2]; - m_recordQueue.Read(lengthBytes, 0, 2, 11); - length = TlsUtilities.ReadUint16(lengthBytes, 0); + length = m_recordQueue.ReadUint16(11); } int received = System.Math.Min(m_recordQueue.Available, RECORD_HEADER_LENGTH + length); @@ -754,9 +883,16 @@ namespace Org.BouncyCastle.Tls { MemoryStream output = new MemoryStream(); heartbeatMessage.Encode(output); - byte[] buf = output.ToArray(); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (!output.TryGetBuffer(out var buffer)) + throw new InvalidOperationException(); + + SendRecord(ContentType.heartbeat, buffer); +#else + byte[] buf = output.ToArray(); SendRecord(ContentType.heartbeat, buf, 0, buf.Length); +#endif } /* @@ -766,12 +902,20 @@ namespace Org.BouncyCastle.Tls * be possible reordering of records (which might surprise a reliable transport implementation). */ /// <exception cref="IOException"/> +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void SendRecord(short contentType, ReadOnlySpan<byte> buffer) +#else private void SendRecord(short contentType, byte[] buf, int off, int len) +#endif { // Never send anything until a valid ClientHello has been received if (m_writeVersion == null) return; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int len = buffer.Length; +#endif + if (len > m_plaintextLimit) throw new TlsFatalAlert(AlertDescription.internal_error); @@ -789,8 +933,13 @@ namespace Org.BouncyCastle.Tls long macSequenceNumber = GetMacSequenceNumber(recordEpoch, recordSequenceNumber); ProtocolVersion recordVersion = m_writeVersion; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + TlsEncodeResult encoded = m_writeEpoch.Cipher.EncodePlaintext(macSequenceNumber, contentType, + recordVersion, RECORD_HEADER_LENGTH, buffer); +#else TlsEncodeResult encoded = m_writeEpoch.Cipher.EncodePlaintext(macSequenceNumber, contentType, recordVersion, RECORD_HEADER_LENGTH, buf, off, len); +#endif int ciphertextLength = encoded.len - RECORD_HEADER_LENGTH; TlsUtilities.CheckUint16(ciphertextLength); diff --git a/crypto/src/tls/DtlsTransport.cs b/crypto/src/tls/DtlsTransport.cs index 6d481702f..033e0af0b 100644 --- a/crypto/src/tls/DtlsTransport.cs +++ b/crypto/src/tls/DtlsTransport.cs @@ -37,6 +37,10 @@ namespace Org.BouncyCastle.Tls throw new ArgumentException("invalid offset: " + off, "off"); if (len < 0 || len > buf.Length - off) throw new ArgumentException("invalid length: " + len, "len"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Receive(buf.AsSpan(off, len), waitMillis); +#else if (waitMillis < 0) throw new ArgumentException("cannot be negative", "waitMillis"); @@ -79,8 +83,58 @@ namespace Org.BouncyCastle.Tls m_recordLayer.Fail(AlertDescription.internal_error); throw new TlsFatalAlert(AlertDescription.internal_error, e); } +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + /// <exception cref="IOException"/> + public virtual int Receive(Span<byte> buffer, int waitMillis) + { + if (waitMillis < 0) + throw new ArgumentException("cannot be negative", nameof(waitMillis)); + + try + { + return m_recordLayer.Receive(buffer, waitMillis); + } + catch (TlsFatalAlert fatalAlert) + { + if (m_ignoreCorruptRecords && AlertDescription.bad_record_mac == fatalAlert.AlertDescription) + return -1; + + m_recordLayer.Fail(fatalAlert.AlertDescription); + throw fatalAlert; + } + catch (TlsTimeoutException e) + { + throw e; + } + catch (SocketException e) + { + if (TlsUtilities.IsTimeout(e)) + throw e; + + m_recordLayer.Fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + // TODO[tls-port] Can we support interrupted IO on .NET? + //catch (InterruptedIOException e) + //{ + // throw e; + //} + catch (IOException e) + { + m_recordLayer.Fail(AlertDescription.internal_error); + throw e; + } + catch (Exception e) + { + m_recordLayer.Fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + } +#endif + /// <exception cref="IOException"/> public virtual void Send(byte[] buf, int off, int len) { @@ -91,6 +145,9 @@ namespace Org.BouncyCastle.Tls if (len < 0 || len > buf.Length - off) throw new ArgumentException("invalid length: " + len, "len"); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Send(buf.AsSpan(off, len)); +#else try { m_recordLayer.Send(buf, off, len); @@ -127,7 +184,50 @@ namespace Org.BouncyCastle.Tls m_recordLayer.Fail(AlertDescription.internal_error); throw new TlsFatalAlert(AlertDescription.internal_error, e); } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public virtual void Send(ReadOnlySpan<byte> buffer) + { + try + { + m_recordLayer.Send(buffer); + } + catch (TlsFatalAlert fatalAlert) + { + m_recordLayer.Fail(fatalAlert.AlertDescription); + throw fatalAlert; + } + catch (TlsTimeoutException e) + { + throw e; + } + catch (SocketException e) + { + if (TlsUtilities.IsTimeout(e)) + throw e; + + m_recordLayer.Fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + // TODO[tls-port] Can we support interrupted IO on .NET? + //catch (InterruptedIOException e) + //{ + // throw e; + //} + catch (IOException e) + { + m_recordLayer.Fail(AlertDescription.internal_error); + throw e; + } + catch (Exception e) + { + m_recordLayer.Fail(AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } } +#endif /// <exception cref="IOException"/> public virtual void Close() diff --git a/crypto/src/tls/TlsUtilities.cs b/crypto/src/tls/TlsUtilities.cs index f12198082..463928ba6 100644 --- a/crypto/src/tls/TlsUtilities.cs +++ b/crypto/src/tls/TlsUtilities.cs @@ -747,9 +747,16 @@ namespace Org.BouncyCastle.Tls public static short ReadUint8(byte[] buf, int offset) { - return (short)(buf[offset] & 0xff); + return (short)buf[offset]; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static short ReadUint8(ReadOnlySpan<byte> buffer) + { + return (short)buffer[0]; + } +#endif + public static int ReadUint16(Stream input) { int i1 = input.ReadByte(); diff --git a/crypto/test/src/tls/test/LoggingDatagramTransport.cs b/crypto/test/src/tls/test/LoggingDatagramTransport.cs index f675b72fc..0ad15e065 100644 --- a/crypto/test/src/tls/test/LoggingDatagramTransport.cs +++ b/crypto/test/src/tls/test/LoggingDatagramTransport.cs @@ -34,25 +34,86 @@ namespace Org.BouncyCastle.Tls.Tests public virtual int Receive(byte[] buf, int off, int len, int waitMillis) { +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + return Receive(buf.AsSpan(off, len), waitMillis); +#else int length = m_transport.Receive(buf, off, len, waitMillis); if (length >= 0) { DumpDatagram("Received", buf, off, length); } return length; +#endif } +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + public virtual int Receive(Span<byte> buffer, int waitMillis) + { + int length = m_transport.Receive(buffer, waitMillis); + if (length >= 0) + { + DumpDatagram("Received", buffer[..length]); + } + return length; + } +#endif + public virtual void Send(byte[] buf, int off, int len) { +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + Send(buf.AsSpan(off, len)); +#else DumpDatagram("Sending", buf, off, len); m_transport.Send(buf, off, len); +#endif } +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + public virtual void Send(ReadOnlySpan<byte> buffer) + { + DumpDatagram("Sending", buffer); + m_transport.Send(buffer); + } +#endif + public virtual void Close() { m_transport.Close(); } +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + private void DumpDatagram(string verb, ReadOnlySpan<byte> buffer) + { + int len = buffer.Length; + long timestamp = DateTimeUtilities.CurrentUnixMs() - m_launchTimestamp; + StringBuilder sb = new StringBuilder("(+" + timestamp + "ms) " + verb + " " + len + " byte datagram:"); + for (int pos = 0; pos < len; ++pos) + { + if (pos % 16 == 0) + { + sb.Append(Environment.NewLine); + sb.Append(" "); + } + else if (pos % 16 == 8) + { + sb.Append('-'); + } + else + { + sb.Append(' '); + } + int val = buffer[pos] & 0xFF; + sb.Append(HEX_CHARS[val >> 4]); + sb.Append(HEX_CHARS[val & 0xF]); + } + Dump(sb.ToString()); + } +#else private void DumpDatagram(string verb, byte[] buf, int off, int len) { long timestamp = DateTimeUtilities.CurrentUnixMs() - m_launchTimestamp; @@ -78,6 +139,7 @@ namespace Org.BouncyCastle.Tls.Tests } Dump(sb.ToString()); } +#endif private void Dump(string s) { diff --git a/crypto/test/src/tls/test/MockDatagramAssociation.cs b/crypto/test/src/tls/test/MockDatagramAssociation.cs index ef317c7b6..3612bec40 100644 --- a/crypto/test/src/tls/test/MockDatagramAssociation.cs +++ b/crypto/test/src/tls/test/MockDatagramAssociation.cs @@ -58,6 +58,10 @@ namespace Org.BouncyCastle.Tls.Tests public virtual int Receive(byte[] buf, int off, int len, int waitMillis) { +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + return Receive(buf.AsSpan(off, len), waitMillis); +#else lock (m_receiveQueue) { if (m_receiveQueue.Count < 1) @@ -81,10 +85,45 @@ namespace Org.BouncyCastle.Tls.Tests Array.Copy(packet, 0, buf, off, copyLength); return copyLength; } +#endif } +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + public virtual int Receive(Span<byte> buffer, int waitMillis) + { + lock (m_receiveQueue) + { + if (m_receiveQueue.Count < 1) + { + try + { + Monitor.Wait(m_receiveQueue, waitMillis); + } + catch (ThreadInterruptedException) + { + // TODO Keep waiting until full wait expired? + } + + if (m_receiveQueue.Count < 1) + return -1; + } + + byte[] packet = m_receiveQueue[0]; + m_receiveQueue.RemoveAt(0); + int copyLength = System.Math.Min(buffer.Length, packet.Length); + packet.AsSpan(0, copyLength).CopyTo(buffer); + return copyLength; + } + } +#endif + public virtual void Send(byte[] buf, int off, int len) { +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + Send(buf.AsSpan(off, len)); +#else if (len > m_outer.m_mtu) { // TODO Simulate rejection? @@ -97,7 +136,27 @@ namespace Org.BouncyCastle.Tls.Tests m_sendQueue.Add(packet); Monitor.PulseAll(m_sendQueue); } +#endif + } + +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + public virtual void Send(ReadOnlySpan<byte> buffer) + { + if (buffer.Length > m_outer.m_mtu) + { + // TODO Simulate rejection? + } + + byte[] packet = buffer.ToArray(); + + lock (m_sendQueue) + { + m_sendQueue.Add(packet); + Monitor.PulseAll(m_sendQueue); + } } +#endif public virtual void Close() { diff --git a/crypto/test/src/tls/test/UnreliableDatagramTransport.cs b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs index bdbfd6e67..7769db9d1 100644 --- a/crypto/test/src/tls/test/UnreliableDatagramTransport.cs +++ b/crypto/test/src/tls/test/UnreliableDatagramTransport.cs @@ -37,6 +37,10 @@ namespace Org.BouncyCastle.Tls.Tests public virtual int Receive(byte[] buf, int off, int len, int waitMillis) { +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + return Receive(buf.AsSpan(off, len), waitMillis); +#else long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis; for (;;) { @@ -52,10 +56,37 @@ namespace Org.BouncyCastle.Tls.Tests waitMillis = (int)(endMillis - now); } +#endif } +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + public virtual int Receive(Span<byte> buffer, int waitMillis) + { + long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis; + for (;;) + { + int length = m_transport.Receive(buffer, waitMillis); + if (length < 0 || !LostPacket(m_percentPacketLossReceiving)) + return length; + + Console.WriteLine("PACKET LOSS (" + length + " byte packet not received)"); + + long now = DateTimeUtilities.CurrentUnixMs(); + if (now >= endMillis) + return -1; + + waitMillis = (int)(endMillis - now); + } + } +#endif + public virtual void Send(byte[] buf, int off, int len) { +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + Send(buf.AsSpan(off, len)); +#else if (LostPacket(m_percentPacketLossSending)) { Console.WriteLine("PACKET LOSS (" + len + " byte packet not sent)"); @@ -64,7 +95,23 @@ namespace Org.BouncyCastle.Tls.Tests { m_transport.Send(buf, off, len); } +#endif + } + +//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET6_0_OR_GREATER + public virtual void Send(ReadOnlySpan<byte> buffer) + { + if (LostPacket(m_percentPacketLossSending)) + { + Console.WriteLine("PACKET LOSS (" + buffer.Length + " byte packet not sent)"); + } + else + { + m_transport.Send(buffer); + } } +#endif public virtual void Close() { |