First working built-in http proxy
This commit is contained in:
@@ -1,15 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.RegularExpressions;
|
||||
using Shadowsocks.ForwardProxy;
|
||||
using Shadowsocks.Util.Sockets;
|
||||
|
||||
namespace Shadowsocks.Controller.Service
|
||||
{
|
||||
class Http2Socks5 : Listener.Service
|
||||
{
|
||||
|
||||
private readonly ByteSearch.SearchTarget _connectSearch =
|
||||
new ByteSearch.SearchTarget(Encoding.UTF8.GetBytes("HTTP"));
|
||||
|
||||
private readonly int _socks5Port;
|
||||
|
||||
public Http2Socks5(int socks5Port)
|
||||
{
|
||||
_socks5Port = socks5Port;
|
||||
}
|
||||
|
||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
{
|
||||
if (socket.ProtocolType != ProtocolType.Tcp)
|
||||
@@ -17,7 +28,465 @@ namespace Shadowsocks.Controller.Service
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (_connectSearch.SearchIn(firstPacket, 0, length) != -1)
|
||||
{
|
||||
new HttpHandler(_socks5Port, firstPacket, length, socket);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class HttpHandler
|
||||
{
|
||||
private const string HTTP_CRLF = "\r\n";
|
||||
|
||||
private const string HTTP_CONNECT_200 =
|
||||
"HTTP/1.1 200 Connection established" + HTTP_CRLF +
|
||||
"Proxy-Connection: close" + HTTP_CRLF +
|
||||
"Proxy-Agent: Shadowsocks" + HTTP_CRLF +
|
||||
"" + HTTP_CRLF; // End with an empty line
|
||||
|
||||
private readonly WrappedSocket _localSocket;
|
||||
private readonly int _socks5Port;
|
||||
private Socks5Proxy _socks5;
|
||||
|
||||
|
||||
private bool _closed = false;
|
||||
private bool _localShutdown = false;
|
||||
private bool _remoteShutdown = false;
|
||||
private readonly object _Lock = new object();
|
||||
|
||||
|
||||
private const int RecvSize = 16384;
|
||||
// remote receive buffer
|
||||
private readonly byte[] _remoteRecvBuffer = new byte[RecvSize];
|
||||
// connection receive buffer
|
||||
private readonly byte[] _connetionRecvBuffer = new byte[RecvSize];
|
||||
|
||||
|
||||
public HttpHandler(int socks5Port, byte[] firstPacket, int length, Socket socket)
|
||||
{
|
||||
_socks5Port = socks5Port;
|
||||
_localSocket = new WrappedSocket(socket);
|
||||
_localSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
||||
new LineReader(_localSocket, firstPacket, 0, length, OnLineRead, OnException, OnFinish,
|
||||
Encoding.UTF8, HTTP_CRLF, 1024, null);
|
||||
}
|
||||
|
||||
private void CheckClose()
|
||||
{
|
||||
if (_localShutdown && _remoteShutdown)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
lock (_Lock)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_closed = true;
|
||||
}
|
||||
|
||||
_localSocket.Dispose();
|
||||
_socks5?.Close();
|
||||
}
|
||||
|
||||
private byte[] _lastBytes;
|
||||
private int _lastBytesIndex;
|
||||
private int _lastBytesLength;
|
||||
|
||||
#region Socks5 Process
|
||||
|
||||
private void ProxyConnectCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_socks5.EndConnectProxy(ar);
|
||||
|
||||
_socks5.BeginConnectDest(SocketUtil.GetEndPoint(_targetHost, _targetPort), ConnectCallback, null);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_socks5.EndConnectDest(ar);
|
||||
|
||||
if (_isConnect)
|
||||
{
|
||||
// http connect response
|
||||
SendConnectResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
// send header
|
||||
SendHeader();
|
||||
}
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CONNECT
|
||||
|
||||
private void SendConnectResponse()
|
||||
{
|
||||
var b = Encoding.UTF8.GetBytes(HTTP_CONNECT_200);
|
||||
_localSocket.BeginSend(b, 0, b.Length, SocketFlags.None, Http200SendCallback, null);
|
||||
}
|
||||
|
||||
private void Http200SendCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_localSocket.EndSend(ar);
|
||||
|
||||
StartPipe();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Other http method except CONNECT
|
||||
|
||||
private void SendHeader()
|
||||
{
|
||||
var h = _headers.Dequeue() + HTTP_CRLF;
|
||||
var len = Encoding.UTF8.GetBytes(h, 0, h.Length, _connetionRecvBuffer, 0);
|
||||
_socks5.BeginSend(_connetionRecvBuffer, 0, len, SocketFlags.None, HeaderSendCallback, null);
|
||||
|
||||
}
|
||||
|
||||
private void HeaderSendCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_socks5.EndSend(ar);
|
||||
|
||||
if (_headers.Count > 0)
|
||||
{
|
||||
SendHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartPipe();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Pipe
|
||||
|
||||
private void StartPipe()
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_socks5.BeginReceive(_remoteRecvBuffer, 0, RecvSize, 0,
|
||||
PipeRemoteReceiveCallback, null);
|
||||
|
||||
if (_lastBytesLength > 0)
|
||||
{
|
||||
_socks5.BeginSend(_lastBytes, _lastBytesIndex, _lastBytesLength, SocketFlags.None,
|
||||
PipeRemoteSendCallback, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_localSocket.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0,
|
||||
PipeConnectionReceiveCallback, null);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void PipeRemoteReceiveCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
int bytesRead = _socks5.EndReceive(ar);
|
||||
Logging.Debug("Read from remote: " + bytesRead);
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
_localSocket.BeginSend(_remoteRecvBuffer, 0, bytesRead, 0, PipeConnectionSendCallback, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_localSocket.Shutdown(SocketShutdown.Send);
|
||||
_localShutdown = true;
|
||||
CheckClose();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void PipeConnectionReceiveCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
int bytesRead = _localSocket.EndReceive(ar);
|
||||
Logging.Debug("Read from local: " + bytesRead);
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
_socks5.BeginSend(_connetionRecvBuffer, 0, bytesRead, 0, PipeRemoteSendCallback, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_socks5.Shutdown(SocketShutdown.Send);
|
||||
_remoteShutdown = true;
|
||||
CheckClose();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void PipeRemoteSendCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
_socks5.EndSend(ar);
|
||||
_localSocket.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0,
|
||||
PipeConnectionReceiveCallback, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void PipeConnectionSendCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
_localSocket.EndSend(ar);
|
||||
_socks5.BeginReceive(_remoteRecvBuffer, 0, RecvSize, 0,
|
||||
PipeRemoteReceiveCallback, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Header Parse
|
||||
|
||||
private void OnException(Exception ex, object state)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
|
||||
private static readonly Regex HttpRequestHeaderRegex = new Regex(@"^([A-Z]+?) ([^\s]+) HTTP/1\.\d$");
|
||||
|
||||
private int _requestLineCount = 0;
|
||||
private volatile bool _isConnect = false;
|
||||
|
||||
private string _targetHost;
|
||||
private int _targetPort;
|
||||
private readonly Queue<string> _headers = new Queue<string>();
|
||||
|
||||
private bool OnLineRead(string line, object state)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.Debug(line);
|
||||
|
||||
if (_requestLineCount == 0)
|
||||
{
|
||||
var m = HttpRequestHeaderRegex.Match(line);
|
||||
if (m.Success)
|
||||
{
|
||||
var method = m.Groups[1].Value;
|
||||
|
||||
if (method == "CONNECT")
|
||||
{
|
||||
_isConnect = true;
|
||||
|
||||
var location = m.Groups[2].Value;
|
||||
var locs = location.Split(':');
|
||||
_targetHost = locs[0];
|
||||
if (locs.Length > 1)
|
||||
{
|
||||
if (!int.TryParse(locs[1], out _targetPort))
|
||||
{
|
||||
throw new Exception("Bad http header: " + line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_targetPort = 80;
|
||||
}
|
||||
}
|
||||
|
||||
_headers.Enqueue(line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line.IsNullOrEmpty())
|
||||
{
|
||||
_headers.Enqueue("");
|
||||
return true;
|
||||
}
|
||||
if (!line.StartsWith("Proxy-"))
|
||||
{
|
||||
_headers.Enqueue(line);
|
||||
}
|
||||
|
||||
if (!_isConnect)
|
||||
{
|
||||
if (line.StartsWith("Host: "))
|
||||
{
|
||||
var location = line.Substring(6).Trim();
|
||||
var locs = location.Split(':');
|
||||
_targetHost = locs[0];
|
||||
if (locs.Length > 1)
|
||||
{
|
||||
if (!int.TryParse(locs[1], out _targetPort))
|
||||
{
|
||||
throw new Exception("Bad http header: " + line);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_targetPort = 80;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_requestLineCount++;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnFinish(byte[] lastBytes, int index, int length, object state)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_targetHost == null)
|
||||
{
|
||||
Logging.Error("Unkonwn host");
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (length > 0)
|
||||
{
|
||||
_lastBytes = lastBytes;
|
||||
_lastBytesIndex = index;
|
||||
_lastBytesLength = length;
|
||||
}
|
||||
|
||||
// Start socks5 conn
|
||||
_socks5 = new Socks5Proxy();
|
||||
|
||||
_socks5.BeginConnectProxy(SocketUtil.GetEndPoint("127.0.0.1", _socks5Port), ProxyConnectCallback,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Shadowsocks.Controller.Service;
|
||||
using Shadowsocks.Controller.Strategy;
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Properties;
|
||||
@@ -462,6 +462,7 @@ namespace Shadowsocks.Controller
|
||||
services.Add(tcpRelay);
|
||||
services.Add(udpRelay);
|
||||
services.Add(_pacServer);
|
||||
services.Add(new Http2Socks5(_config.localPort));
|
||||
_listener = new Listener(services);
|
||||
_listener.Start(_config);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
|
||||
namespace Shadowsocks.Util.Sockets
|
||||
{
|
||||
// Boyer-Moore string search
|
||||
public static class ByteSearch
|
||||
{
|
||||
public class SearchTarget
|
||||
{
|
||||
private readonly byte[] _needle;
|
||||
private readonly int[] _needleCharTable;
|
||||
private readonly int[] _needleOffsetTable;
|
||||
|
||||
public SearchTarget(byte[] needle)
|
||||
{
|
||||
_needle = needle;
|
||||
_needleCharTable = MakeCharTable(needle);
|
||||
_needleOffsetTable = MakeOffsetTable(needle);
|
||||
}
|
||||
|
||||
// Thread-safe
|
||||
public int SearchIn(byte[] haystack, int index, int length)
|
||||
{
|
||||
return IndexOf(haystack, index, length, _needle, _needleOffsetTable, _needleCharTable);
|
||||
}
|
||||
}
|
||||
|
||||
private static int IndexOf(byte[] haystack, int index, int length, byte[] needle, int[] offsetTable, int[] charTable)
|
||||
{
|
||||
|
||||
var end = index + length;
|
||||
for (int i = needle.Length - 1 + index, j; i < end;)
|
||||
{
|
||||
for (j = needle.Length - 1; needle[j] == haystack[i]; --i, --j)
|
||||
{
|
||||
if (j == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// i += needle.length - j; // For naive method
|
||||
i += Math.Max(offsetTable[needle.Length - 1 - j], charTable[haystack[i]]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the jump table based on the mismatched character information.
|
||||
*/
|
||||
private static int[] MakeCharTable(byte[] needle)
|
||||
{
|
||||
const int ALPHABET_SIZE = 256;
|
||||
int[] table = new int[ALPHABET_SIZE];
|
||||
for (int i = 0; i < table.Length; ++i)
|
||||
{
|
||||
table[i] = needle.Length;
|
||||
}
|
||||
for (int i = 0; i < needle.Length - 1; ++i)
|
||||
{
|
||||
table[needle[i]] = needle.Length - 1 - i;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the jump table based on the scan offset which mismatch occurs.
|
||||
*/
|
||||
private static int[] MakeOffsetTable(byte[] needle)
|
||||
{
|
||||
int[] table = new int[needle.Length];
|
||||
int lastPrefixPosition = needle.Length;
|
||||
for (int i = needle.Length - 1; i >= 0; --i)
|
||||
{
|
||||
if (IsPrefix(needle, i + 1))
|
||||
{
|
||||
lastPrefixPosition = i + 1;
|
||||
}
|
||||
table[needle.Length - 1 - i] = lastPrefixPosition - i + needle.Length - 1;
|
||||
}
|
||||
for (int i = 0; i < needle.Length - 1; ++i)
|
||||
{
|
||||
int slen = SuffixLength(needle, i);
|
||||
table[slen] = needle.Length - 1 - i + slen;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is needle[p:end] a prefix of needle?
|
||||
*/
|
||||
private static bool IsPrefix(byte[] needle, int p)
|
||||
{
|
||||
for (int i = p, j = 0; i < needle.Length; ++i, ++j)
|
||||
{
|
||||
if (needle[i] != needle[j])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum length of the substring ends at p and is a suffix.
|
||||
*/
|
||||
private static int SuffixLength(byte[] needle, int p)
|
||||
{
|
||||
int len = 0;
|
||||
for (int i = p, j = needle.Length - 1;
|
||||
i >= 0 && needle[i] == needle[j]; --i, --j)
|
||||
{
|
||||
len += 1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,7 @@ namespace Shadowsocks.Util.Sockets
|
||||
private readonly Encoding _encoding;
|
||||
// private readonly string _delimiter;
|
||||
private readonly byte[] _delimiterBytes;
|
||||
private readonly int[] _delimiterSearchCharTable;
|
||||
private readonly int[] _delimiterSearchOffsetTable;
|
||||
private readonly ByteSearch.SearchTarget _delimiterSearch;
|
||||
private readonly object _state;
|
||||
|
||||
private readonly byte[] _lineBuffer;
|
||||
@@ -78,8 +77,7 @@ namespace Shadowsocks.Util.Sockets
|
||||
throw new ArgumentException("Too small!", nameof(maxLineBytes));
|
||||
}
|
||||
|
||||
_delimiterSearchCharTable = MakeCharTable(_delimiterBytes);
|
||||
_delimiterSearchOffsetTable = MakeOffsetTable(_delimiterBytes);
|
||||
_delimiterSearch = new ByteSearch.SearchTarget(_delimiterBytes);
|
||||
|
||||
_lineBuffer = new byte[maxLineBytes];
|
||||
|
||||
@@ -140,8 +138,7 @@ namespace Shadowsocks.Util.Sockets
|
||||
private void NewPackageRecv()
|
||||
{
|
||||
int i;
|
||||
while ((i = IndexOf(_lineBuffer, _bufferDataIndex, _bufferDataLength, _delimiterBytes, _delimiterSearchOffsetTable,
|
||||
_delimiterSearchCharTable)) != -1)
|
||||
while ((i = _delimiterSearch.SearchIn(_lineBuffer, _bufferDataIndex, _bufferDataLength)) != -1)
|
||||
{
|
||||
var decodeLen = i - _bufferDataIndex;
|
||||
string line = _encoding.GetString(_lineBuffer, _bufferDataIndex, decodeLen);
|
||||
@@ -183,97 +180,5 @@ namespace Shadowsocks.Util.Sockets
|
||||
{
|
||||
_onFinish?.Invoke(_lineBuffer, _bufferDataIndex, _bufferDataLength, _state);
|
||||
}
|
||||
|
||||
#region Boyer-Moore string search
|
||||
|
||||
private static int IndexOf(byte[] haystack, int index, int length, byte[] needle, int[] offsetTable, int[] charTable)
|
||||
{
|
||||
var end = index + length;
|
||||
for (int i = needle.Length - 1 + index, j; i < end;)
|
||||
{
|
||||
for (j = needle.Length - 1; needle[j] == haystack[i]; --i, --j)
|
||||
{
|
||||
if (j == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// i += needle.length - j; // For naive method
|
||||
i += Math.Max(offsetTable[needle.Length - 1 - j], charTable[haystack[i]]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the jump table based on the mismatched character information.
|
||||
*/
|
||||
private static int[] MakeCharTable(byte[] needle)
|
||||
{
|
||||
const int ALPHABET_SIZE = 256;
|
||||
int[] table = new int[ALPHABET_SIZE];
|
||||
for (int i = 0; i < table.Length; ++i)
|
||||
{
|
||||
table[i] = needle.Length;
|
||||
}
|
||||
for (int i = 0; i < needle.Length - 1; ++i)
|
||||
{
|
||||
table[needle[i]] = needle.Length - 1 - i;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the jump table based on the scan offset which mismatch occurs.
|
||||
*/
|
||||
private static int[] MakeOffsetTable(byte[] needle)
|
||||
{
|
||||
int[] table = new int[needle.Length];
|
||||
int lastPrefixPosition = needle.Length;
|
||||
for (int i = needle.Length - 1; i >= 0; --i)
|
||||
{
|
||||
if (IsPrefix(needle, i + 1))
|
||||
{
|
||||
lastPrefixPosition = i + 1;
|
||||
}
|
||||
table[needle.Length - 1 - i] = lastPrefixPosition - i + needle.Length - 1;
|
||||
}
|
||||
for (int i = 0; i < needle.Length - 1; ++i)
|
||||
{
|
||||
int slen = SuffixLength(needle, i);
|
||||
table[slen] = needle.Length - 1 - i + slen;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is needle[p:end] a prefix of needle?
|
||||
*/
|
||||
private static bool IsPrefix(byte[] needle, int p)
|
||||
{
|
||||
for (int i = p, j = 0; i < needle.Length; ++i, ++j)
|
||||
{
|
||||
if (needle[i] != needle[j])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum length of the substring ends at p and is a suffix.
|
||||
*/
|
||||
private static int SuffixLength(byte[] needle, int p)
|
||||
{
|
||||
int len = 0;
|
||||
for (int i = p, j = needle.Length - 1;
|
||||
i >= 0 && needle[i] == needle[j]; --i, --j)
|
||||
{
|
||||
len += 1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,14 @@ namespace Shadowsocks.Util.Sockets
|
||||
private Socket _activeSocket;
|
||||
|
||||
|
||||
public WrappedSocket() { }
|
||||
|
||||
public WrappedSocket(Socket socket)
|
||||
{
|
||||
_activeSocket = socket;
|
||||
}
|
||||
|
||||
|
||||
public void BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state)
|
||||
{
|
||||
if (_disposed)
|
||||
|
||||
@@ -190,6 +190,7 @@
|
||||
<Compile Include="StringEx.cs" />
|
||||
<Compile Include="Controller\System\Hotkeys\Hotkeys.cs" />
|
||||
<Compile Include="Encryption\RNG.cs" />
|
||||
<Compile Include="Util\Sockets\ByteSearch.cs" />
|
||||
<Compile Include="Util\Sockets\LineReader.cs" />
|
||||
<Compile Include="Util\Sockets\SocketUtil.cs" />
|
||||
<Compile Include="Util\Sockets\WrappedSocket.cs" />
|
||||
|
||||
Reference in New Issue
Block a user