diff --git a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs b/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs index 365ea1d0..08464db0 100644 --- a/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs +++ b/shadowsocks-csharp/Encryption/AEAD/AEADEncryptor.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Text; -using Cyotek.Collections.Generic; +using Shadowsocks.Encryption.CircularBuffer; using Shadowsocks.Controller; using Shadowsocks.Encryption.Exception; using Shadowsocks.Encryption.Stream; @@ -21,8 +21,8 @@ namespace Shadowsocks.Encryption.AEAD protected static byte[] _udpTmpBuf = new byte[65536]; // every connection should create its own buffer - private CircularBuffer _encCircularBuffer = new CircularBuffer(MAX_INPUT_SIZE * 2, false); - private CircularBuffer _decCircularBuffer = new CircularBuffer(MAX_INPUT_SIZE * 2, false); + private ByteCircularBuffer _encCircularBuffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); + private ByteCircularBuffer _decCircularBuffer = new ByteCircularBuffer(MAX_INPUT_SIZE * 2); public const int CHUNK_LEN_BYTES = 2; public const uint CHUNK_LEN_MASK = 0x3FFFu; @@ -254,7 +254,7 @@ namespace Shadowsocks.Encryption.AEAD // we have enough data to decrypt one chunk // drop chunk len and its tag from buffer - _decCircularBuffer.Get(CHUNK_LEN_BYTES + tagLen); + _decCircularBuffer.Skip(CHUNK_LEN_BYTES + tagLen); byte[] encChunkBytes = _decCircularBuffer.Get(chunkLen + tagLen); byte[] decChunkBytes = new byte[chunkLen]; uint decChunkLen = 0; diff --git a/shadowsocks-csharp/Encryption/CircularBuffer/ByteCircularBuffer.cs b/shadowsocks-csharp/Encryption/CircularBuffer/ByteCircularBuffer.cs new file mode 100644 index 00000000..61ec2dda --- /dev/null +++ b/shadowsocks-csharp/Encryption/CircularBuffer/ByteCircularBuffer.cs @@ -0,0 +1,510 @@ +#region Original License + +//New BSD License(BSD) +// +//Copyright(c) 2014-2015 Cyotek Ltd +//Copyright(c) 2012, Alex Regueiro +//All rights reserved. +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Cyotek nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +//DISCLAIMED.IN NO EVENT SHALL BE LIABLE FOR ANY +//DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +//ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; + +namespace Shadowsocks.Encryption.CircularBuffer +{ + /// + /// Represents a first-in, first-out collection of objects using a fixed buffer. + /// + /// + /// The capacity of a is the number of elements the can hold. + /// ByteCircularBuffer accepts null as a valid value for reference types and allows duplicate elements. + /// The methods will remove the items that are returned from the ByteCircularBuffer. To view the contents of the ByteCircularBuffer without removing items, use the or methods. + /// + public class ByteCircularBuffer + { + // based on http://circularbuffer.codeplex.com/ + // http://en.wikipedia.org/wiki/Circular_buffer + // modified from https://github.com/cyotek/Cyotek.Collections.Generic.CircularBuffer + // some code taken from https://github.com/xorxornop/RingBuffer + // and https://github.com/xorxornop/PerfCopy + + #region Instance Fields + + private byte[] _buffer; + + private int _capacity; + + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class that is empty and has the specified initial capacity. + /// + /// The maximum capcity of the buffer. + /// Thown if the is less than zero. + public ByteCircularBuffer(int capacity) + { + if (capacity < 0) + { + throw new ArgumentException("The buffer capacity must be greater than or equal to zero.", + nameof(capacity)); + } + + _buffer = new byte[capacity]; + this.Capacity = capacity; + this.Size = 0; + this.Head = 0; + this.Tail = 0; + } + + #endregion + + #region Public Properties + + /// + /// Gets or sets the total number of elements the internal data structure can hold. + /// + /// The total number of elements that the can contain. + /// Thrown if the specified new capacity is smaller than the current contents of the buffer. + public int Capacity + { + get { return _capacity; } + set + { + if (value != _capacity) + { + if (value < this.Size) + { + throw new ArgumentOutOfRangeException(nameof(value), value, + "The new capacity must be greater than or equal to the buffer size."); + } + + var newBuffer = new byte[value]; + if (this.Size > 0) + { + this.CopyTo(newBuffer); + } + + _buffer = newBuffer; + + _capacity = value; + } + } + } + + /// + /// Gets the index of the beginning of the buffer data. + /// + /// The index of the first element in the buffer. + public int Head { get; protected set; } + + /// + /// Gets a value indicating whether the buffer is empty. + /// + /// true if buffer is empty; otherwise, false. + public virtual bool IsEmpty => this.Size == 0; + + /// + /// Gets a value indicating whether the buffer is full. + /// + /// true if the buffer is full; otherwise, false. + /// The property always returns false if the property is set to true. + public virtual bool IsFull => this.Size == this.Capacity; + + /// + /// Gets the number of elements contained in the . + /// + /// The number of elements contained in the . + public int Size { get; protected set; } + + /// + /// Gets the index of the end of the buffer data. + /// + /// The index of the last element in the buffer. + public int Tail { get; protected set; } + + #endregion + + #region Public Members + + /// + /// Removes all items from the . + /// + public void Clear() + { + this.Size = 0; + this.Head = 0; + this.Tail = 0; + _buffer = new byte[this.Capacity]; + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// true if is found in the ; otherwise, false. + public bool Contains(byte item) + { + var bufferIndex = this.Head; + var comparer = EqualityComparer.Default; + var result = false; + + for (int i = 0; i < this.Size; i++, bufferIndex++) + { + if (bufferIndex == this.Capacity) + { + bufferIndex = 0; + } + + if (comparer.Equals(_buffer[bufferIndex], item)) + { + result = true; + break; + } + } + + return result; + } + + /// + /// Copies the entire to a compatible one-dimensional array, starting at the beginning of the target array. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + public void CopyTo(byte[] array) + { + this.CopyTo(array, 0); + } + + /// + /// Copies the entire to a compatible one-dimensional array, starting at the specified index of the target array. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + public void CopyTo(byte[] array, int arrayIndex) + { + this.CopyTo(this.Head, array, arrayIndex, Math.Min(this.Size, array.Length - arrayIndex)); + } + + /// + /// Copies a range of elements from the to a compatible one-dimensional array, starting at the specified index of the target array. + /// + /// The zero-based index in the source at which copying begins. + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// The number of elements to copy. + public virtual void CopyTo(int index, byte[] array, int arrayIndex, int count) + { + if (count > this.Size) + { + throw new ArgumentOutOfRangeException(nameof(count), count, + "The read count cannot be greater than the buffer size."); + } + + var startAnchor = index; + var dstIndex = arrayIndex; + + while (count > 0) + { + int chunk = Math.Min(Capacity - startAnchor, count); + Buffer.BlockCopy(_buffer, startAnchor, array, dstIndex, chunk); + startAnchor = (startAnchor + chunk == Capacity) ? 0 : startAnchor + chunk; + dstIndex += chunk; + count -= chunk; + } + } + + /// + /// Removes and returns the specified number of objects from the beginning of the . + /// + /// The number of elements to remove and return from the . + /// The objects that are removed from the beginning of the . + public byte[] Get(int count) + { + if (count <= 0) throw new ArgumentOutOfRangeException("should greater than 0"); + var result = new byte[count]; + + this.Get(result); + + return result; + } + + /// + /// Copies and removes the specified number elements from the to a compatible one-dimensional array, starting at the beginning of the target array. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The actual number of elements copied into . + public int Get(byte[] array) + { + if (array.Length <= 0) throw new ArgumentOutOfRangeException("should greater than 0"); + return this.Get(array, 0, array.Length); + } + + /// + /// Copies and removes the specified number elements from the to a compatible one-dimensional array, starting at the specified index of the target array. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// The number of elements to copy. + /// The actual number of elements copied into . + public virtual int Get(byte[] array, int arrayIndex, int count) + { + if (arrayIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Negative offset specified. Offsets must be positive."); + } + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "Negative count specified. Count must be positive."); + } + if (count > this.Size) + { + throw new ArgumentException("Ringbuffer contents insufficient for take/read operation.", nameof(count)); + } + if (array.Length < arrayIndex + count) + { + throw new ArgumentException("Destination array too small for requested output."); + } + var bytesCopied = 0; + var dstIndex = arrayIndex; + while (count > 0) + { + int chunk = Math.Min(Capacity - this.Head, count); + Buffer.BlockCopy(_buffer, this.Head, array, dstIndex, chunk); + this.Head = (this.Head + chunk == Capacity) ? 0 : this.Head + chunk; + this.Size -= chunk; + dstIndex += chunk; + bytesCopied += chunk; + count -= chunk; + } + return bytesCopied; + } + + /// + /// Removes and returns the object at the beginning of the . + /// + /// The object that is removed from the beginning of the . + /// Thrown if the buffer is empty. + /// This method is similar to the method, but Peek does not modify the . + public virtual byte Get() + { + if (this.IsEmpty) + { + throw new InvalidOperationException("The buffer is empty."); + } + + var item = _buffer[this.Head]; + if (++this.Head == this.Capacity) + { + this.Head = 0; + } + this.Size--; + + return item; + } + + /// + /// Returns the object at the beginning of the without removing it. + /// + /// The object at the beginning of the . + /// Thrown if the buffer is empty. + public virtual byte Peek() + { + if (this.IsEmpty) + { + throw new InvalidOperationException("The buffer is empty."); + } + + var item = _buffer[this.Head]; + + return item; + } + + /// + /// Returns the specified number of objects from the beginning of the . + /// + /// The number of elements to return from the . + /// The objects that from the beginning of the . + /// Thrown if the buffer is empty. + public virtual byte[] Peek(int count) + { + if (this.IsEmpty) + { + throw new InvalidOperationException("The buffer is empty."); + } + + var items = new byte[count]; + this.CopyTo(items); + + return items; + } + + /// + /// Returns the object at the end of the without removing it. + /// + /// The object at the end of the . + /// Thrown if the buffer is empty. + public virtual byte PeekLast() + { + int bufferIndex; + + if (this.IsEmpty) + { + throw new InvalidOperationException("The buffer is empty."); + } + + if (this.Tail == 0) + { + bufferIndex = this.Size - 1; + } + else + { + bufferIndex = this.Tail - 1; + } + + var item = _buffer[bufferIndex]; + + return item; + } + + /// + /// Copies an entire compatible one-dimensional array to the . + /// + /// The one-dimensional that is the source of the elements copied to . The must have zero-based indexing. + /// Thrown if buffer does not have sufficient capacity to put in new items. + /// If plus the size of exceeds the capacity of the and the property is true, the oldest items in the are overwritten with . + public int Put(byte[] array) + { + return this.Put(array, 0, array.Length); + } + + /// + /// Copies a range of elements from a compatible one-dimensional array to the . + /// + /// The one-dimensional that is the source of the elements copied to . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// The number of elements to copy. + /// Thrown if buffer does not have sufficient capacity to put in new items. + /// If plus exceeds the capacity of the and the property is true, the oldest items in the are overwritten with . + public virtual int Put(byte[] array, int arrayIndex, int count) + { + if (count <= 0) throw new ArgumentOutOfRangeException(nameof(count), "Count must be positive."); + if (this.Size + count > this.Capacity) + { + throw new InvalidOperationException("The buffer does not have sufficient capacity to put new items."); + } + + if (array.Length < arrayIndex + count) + { + throw new ArgumentException("Source array too small for requested input."); + } + var srcIndex = arrayIndex; + var bytesToProcess = count; + while (bytesToProcess > 0) + { + int chunk = Math.Min(Capacity - Tail, bytesToProcess); + Buffer.BlockCopy(array, srcIndex, _buffer, Tail, chunk); + Tail = (Tail + chunk == Capacity) ? 0 : Tail + chunk; + this.Size += chunk; + srcIndex += chunk; + bytesToProcess -= chunk; + } + + return count; + } + + /// + /// Adds a byte to the end of the . + /// + /// The byte to add to the . + /// Thrown if buffer does not have sufficient capacity to put in new items. + public virtual void Put(byte item) + { + if (IsFull) + { + throw new InvalidOperationException("The buffer does not have sufficient capacity to put new items."); + } + + _buffer[this.Tail] = item; + + this.Tail++; + if (this.Size == this.Capacity) + { + this.Head++; + if (this.Head >= this.Capacity) + { + this.Head -= this.Capacity; + } + } + + if (this.Tail == this.Capacity) + { + this.Tail = 0; + } + + if (this.Size != this.Capacity) + { + this.Size++; + } + } + + /// + /// Increments the starting index of the data buffer in the . + /// + /// The number of elements to increment the data buffer start index by. + public void Skip(int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count), "Negative count specified. Count must be positive."); + } + if (count > this.Size) + { + throw new ArgumentException("Ringbuffer contents insufficient for operation.", nameof(count)); + } + + // Modular division gives new offset position + this.Head = (this.Head + count) % Capacity; + this.Size -= count; + } + + /// + /// Copies the elements to a new array. + /// + /// A new array containing elements copied from the . + /// The is not modified. The order of the elements in the new array is the same as the order of the elements from the beginning of the to its end. + public byte[] ToArray() + { + var result = new byte[this.Size]; + + this.CopyTo(result); + + return result; + } + + #endregion + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs b/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs index 5c3089c5..a93a7cf8 100644 --- a/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs +++ b/shadowsocks-csharp/Encryption/Stream/StreamEncryptor.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; -using Cyotek.Collections.Generic; +using Shadowsocks.Encryption.CircularBuffer; using Shadowsocks.Controller; namespace Shadowsocks.Encryption.Stream @@ -14,8 +14,8 @@ namespace Shadowsocks.Encryption.Stream protected static byte[] _udpTmpBuf = new byte[65536]; // every connection should create its own buffer - private CircularBuffer _encCircularBuffer = new CircularBuffer(TCPHandler.BufferSize * 2, false); - private CircularBuffer _decCircularBuffer = new CircularBuffer(TCPHandler.BufferSize * 2, false); + private ByteCircularBuffer _encCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); + private ByteCircularBuffer _decCircularBuffer = new ByteCircularBuffer(TCPHandler.BufferSize * 2); protected Dictionary ciphers; @@ -143,7 +143,8 @@ namespace Shadowsocks.Encryption.Stream } byte[] cipher = _decCircularBuffer.ToArray(); cipherUpdate(false, cipher.Length, cipher, outbuf); - _decCircularBuffer.Clear(); + // move pointer only + _decCircularBuffer.Skip(_decCircularBuffer.Size); outlength = cipher.Length; // done the decryption } diff --git a/shadowsocks-csharp/FodyWeavers.xml b/shadowsocks-csharp/FodyWeavers.xml index 13e605b4..625f36e3 100644 --- a/shadowsocks-csharp/FodyWeavers.xml +++ b/shadowsocks-csharp/FodyWeavers.xml @@ -1,5 +1,5 @@ - + - - + + \ No newline at end of file diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index 2ca0b8da..ae7d8f01 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -1,11 +1,10 @@  - - - - + + + - + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 4f354890..1d239ec9 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -66,9 +66,6 @@ app.manifest - - 3rd\Cyotek.CircularBuffer.1.0.2\lib\net20\Cyotek.Collections.Generic.CircularBuffer.dll - 3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll True @@ -76,7 +73,7 @@ - 3rd\Newtonsoft.Json.10.0.1\lib\net45\Newtonsoft.Json.dll + 3rd\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll @@ -100,6 +97,7 @@ + @@ -266,9 +264,9 @@ - + Designer - + @@ -342,12 +340,12 @@ foreach (var item in filesToCleanup) - + - 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + 此项目引用这台计算机上缺少的 NuGet 程序包。使用 NuGet 程序包还原可下载这些程序包。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 - +