Browse Source

Revert "First working built-in http proxy"

This reverts commit 5ba0dffa5d.
tags/3.4.2
noisyfox 7 years ago
parent
commit
09d89b424d
6 changed files with 102 additions and 603 deletions
  1. +3
    -472
      shadowsocks-csharp/Controller/Service/Http2Socks5.cs
  2. +1
    -2
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  3. +0
    -117
      shadowsocks-csharp/Util/Sockets/ByteSearch.cs
  4. +98
    -3
      shadowsocks-csharp/Util/Sockets/LineReader.cs
  5. +0
    -8
      shadowsocks-csharp/Util/Sockets/WrappedSocket.cs
  6. +0
    -1
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 3
- 472
shadowsocks-csharp/Controller/Service/Http2Socks5.cs View File

@@ -1,26 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using Shadowsocks.ForwardProxy;
using System.Threading.Tasks;
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)
@@ -28,465 +17,7 @@ namespace Shadowsocks.Controller.Service
return false;
}

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

return true;
}
}
}

+ 1
- 2
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -8,7 +8,7 @@ using System.Threading;
using System.Web;
using System.Windows.Forms;
using Newtonsoft.Json;
using Shadowsocks.Controller.Service;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Model;
using Shadowsocks.Properties;
@@ -463,7 +463,6 @@ 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
- 117
shadowsocks-csharp/Util/Sockets/ByteSearch.cs View File

@@ -1,117 +0,0 @@
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;
}
}
}

+ 98
- 3
shadowsocks-csharp/Util/Sockets/LineReader.cs View File

@@ -12,7 +12,8 @@ namespace Shadowsocks.Util.Sockets
private readonly Encoding _encoding;
// private readonly string _delimiter;
private readonly byte[] _delimiterBytes;
private readonly ByteSearch.SearchTarget _delimiterSearch;
private readonly int[] _delimiterSearchCharTable;
private readonly int[] _delimiterSearchOffsetTable;
private readonly object _state;

private readonly byte[] _lineBuffer;
@@ -77,7 +78,8 @@ namespace Shadowsocks.Util.Sockets
throw new ArgumentException("Too small!", nameof(maxLineBytes));
}

_delimiterSearch = new ByteSearch.SearchTarget(_delimiterBytes);
_delimiterSearchCharTable = MakeCharTable(_delimiterBytes);
_delimiterSearchOffsetTable = MakeOffsetTable(_delimiterBytes);

_lineBuffer = new byte[maxLineBytes];

@@ -138,7 +140,8 @@ namespace Shadowsocks.Util.Sockets
private void NewPackageRecv()
{
int i;
while ((i = _delimiterSearch.SearchIn(_lineBuffer, _bufferDataIndex, _bufferDataLength)) != -1)
while ((i = IndexOf(_lineBuffer, _bufferDataIndex, _bufferDataLength, _delimiterBytes, _delimiterSearchOffsetTable,
_delimiterSearchCharTable)) != -1)
{
var decodeLen = i - _bufferDataIndex;
string line = _encoding.GetString(_lineBuffer, _bufferDataIndex, decodeLen);
@@ -180,5 +183,97 @@ 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
}
}

+ 0
- 8
shadowsocks-csharp/Util/Sockets/WrappedSocket.cs View File

@@ -26,14 +26,6 @@ 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)


+ 0
- 1
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -190,7 +190,6 @@
<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" />


Loading…
Cancel
Save