Browse Source

http -> shadowsocks protocol directly

No more http->socks5 transform.
tags/3.4.0
noisyfox 8 years ago
parent
commit
b04c59d8e6
8 changed files with 790 additions and 864 deletions
  1. +0
    -487
      shadowsocks-csharp/Controller/Service/Http2Socks5.cs
  2. +264
    -0
      shadowsocks-csharp/Controller/Service/HttpHandler.cs
  3. +293
    -0
      shadowsocks-csharp/Controller/Service/Socks5Handler.cs
  4. +139
    -333
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  5. +2
    -2
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  6. +3
    -40
      shadowsocks-csharp/ForwardProxy/Socks5Proxy.cs
  7. +85
    -0
      shadowsocks-csharp/Util/Sockets/Socks5Util.cs
  8. +4
    -2
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 0
- 487
shadowsocks-csharp/Controller/Service/Http2Socks5.cs View File

@@ -1,487 +0,0 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
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)
{
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(firstPacket, _localSocket, firstPacket, 0, length, OnLineRead, OnException, OnFinish,
Encoding.UTF8, HTTP_CRLF, 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 len = Encoding.UTF8.GetBytes(HTTP_CONNECT_200, 0, HTTP_CONNECT_200.Length, _remoteRecvBuffer, 0);
_localSocket.BeginSend(_remoteRecvBuffer, 0, len, 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);

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);

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 ParseHost(string host)
{
var locs = host.Split(':');
_targetHost = locs[0];
if (locs.Length > 1)
{
if (!int.TryParse(locs[1], out _targetPort))
{
return false;
}
}
else
{
_targetPort = 80;
}

return true;
}

private bool OnLineRead(string line, object state)
{
if (_closed)
{
return true;
}

Logging.Debug(line);

if (!line.StartsWith("Proxy-"))
{
_headers.Enqueue(line);
}

if (_requestLineCount == 0)
{
var m = HttpRequestHeaderRegex.Match(line);
if (m.Success)
{
var method = m.Groups[1].Value;

if (method == "CONNECT")
{
_isConnect = true;

if (!ParseHost(m.Groups[2].Value))
{
throw new Exception("Bad http header: " + line);
}
}
}
}
else
{
if (line.IsNullOrEmpty())
{
return true;
}

if (!_isConnect)
{
if (line.StartsWith("Host: "))
{
if (!ParseHost(line.Substring(6).Trim()))
{
throw new Exception("Bad http header: " + line);
}
}
}
}

_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

}
}
}

+ 264
- 0
shadowsocks-csharp/Controller/Service/HttpHandler.cs View File

@@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Shadowsocks.Model;
using Shadowsocks.Util.Sockets;
using System.Text.RegularExpressions;

namespace Shadowsocks.Controller.Service
{
class HttpHandlerHandlerFactory : ITCPHandlerFactory
{
private static readonly ByteSearch.SearchTarget HttpSearch =
new ByteSearch.SearchTarget(Encoding.UTF8.GetBytes("HTTP"));

public bool CanHandle(byte[] firstPacket, int length)
{
return HttpSearch.SearchIn(firstPacket, 0, length) != -1;
}

public TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
{
return new HttpHandler(controller, config, tcprelay, socket);
}
}

class HttpHandler : TCPHandler
{
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 byte[] _lastBytes;
private int _lastBytesIndex;
private int _lastBytesLength;


public HttpHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) : base(controller, config, tcprelay, socket)
{
_localSocket = new WrappedSocket(socket);
}

public override void StartHandshake(byte[] firstPacket, int length)
{
new LineReader(firstPacket, _localSocket, firstPacket, 0, length, OnLineRead, OnException, OnFinish,
Encoding.UTF8, HTTP_CRLF, null);
}


#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 ParseHost(string host)
{
var locs = host.Split(':');
_targetHost = locs[0];
if (locs.Length > 1)
{
if (!int.TryParse(locs[1], out _targetPort))
{
return false;
}
}
else
{
_targetPort = 80;
}

return true;
}

private bool OnLineRead(string line, object state)
{
if (Closed)
{
return true;
}

Logging.Debug(line);

if (!line.StartsWith("Proxy-"))
{
_headers.Enqueue(line);
}

if (_requestLineCount == 0)
{
var m = HttpRequestHeaderRegex.Match(line);
if (m.Success)
{
var method = m.Groups[1].Value;

if (method == "CONNECT")
{
_isConnect = true;

if (!ParseHost(m.Groups[2].Value))
{
throw new Exception("Bad http header: " + line);
}
}
}
}
else
{
if (line.IsNullOrEmpty())
{
return true;
}

if (!_isConnect)
{
if (line.StartsWith("Host: "))
{
if (!ParseHost(line.Substring(6).Trim()))
{
throw new Exception("Bad http header: " + line);
}
}
}
}

_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;
}

StartConnect(SocketUtil.GetEndPoint(_targetHost, _targetPort));
}
}

#endregion

protected override void OnServerConnected(AsyncSession session)
{
if (_isConnect)
{
// http connect response
SendConnectResponse(session);
}
else
{
// send header
SendHeader(session);
}
}


#region CONNECT

private void SendConnectResponse(AsyncSession session)
{
var len = Encoding.UTF8.GetBytes(HTTP_CONNECT_200, 0, HTTP_CONNECT_200.Length, RemoteRecvBuffer, 0);
_localSocket.BeginSend(RemoteRecvBuffer, 0, len, SocketFlags.None, Http200SendCallback, session);
}

private void Http200SendCallback(IAsyncResult ar)
{
if (Closed)
{
return;
}

try
{
_localSocket.EndSend(ar);

StartPipe((AsyncSession) ar.AsyncState);
}
catch (ArgumentException)
{
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}

#endregion

#region Other http method except CONNECT

private void SendHeader(AsyncSession session)
{
var h = _headers.Dequeue() + HTTP_CRLF;
var len = Encoding.UTF8.GetBytes(h, 0, h.Length, ConnetionRecvBuffer, 0);
BeginSendToServer(len, session, HeaderSendCallback);
}

private void HeaderSendCallback(IAsyncResult ar)
{
if (Closed)
{
return;
}

try
{
var session = EndSendToServer(ar);

if (_headers.Count > 0)
{
SendHeader(session);
}
else
{
StartPipe(session);
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}

#endregion

}
}

+ 293
- 0
shadowsocks-csharp/Controller/Service/Socks5Handler.cs View File

@@ -0,0 +1,293 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using Shadowsocks.Model;
using Shadowsocks.Util.Sockets;

namespace Shadowsocks.Controller.Service
{

class Socks5HandlerFactory : ITCPHandlerFactory
{
public bool CanHandle(byte[] firstPacket, int length)
{
return length >= 2 && firstPacket[0] == 5;
}

public TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
{
return new Socks5Handler(controller, config, tcprelay, socket);
}
}

class Socks5Handler : TCPHandler
{
private byte _command;
private int _firstPacketLength;

public Socks5Handler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) : base(controller, config, tcprelay, socket, false)
{
}

public override void StartHandshake(byte[] firstPacket, int length)
{
if (Closed) return;
try
{
int bytesRead = length;
if (bytesRead > 1)
{
byte[] response = { 5, 0 };
if (firstPacket[0] != 5)
{
// reject socks 4
response = new byte[] { 0, 91 };
Logging.Error("socks 5 protocol error");
}
Connection.BeginSend(response, 0, response.Length, SocketFlags.None, HandshakeSendCallback, null);
}
else
Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}

private void HandshakeSendCallback(IAsyncResult ar)
{
if (Closed) return;
try
{
Connection.EndSend(ar);

// +-----+-----+-------+------+----------+----------+
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +-----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +-----+-----+-------+------+----------+----------+
// Skip first 3 bytes, and read 2 more bytes to analysis the address.
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length.
// TODO validate
Connection.BeginReceive(ConnetionRecvBuffer, 0, 3 + 2, SocketFlags.None,
handshakeReceive2Callback, null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}

private void handshakeReceive2Callback(IAsyncResult ar)
{
if (Closed) return;
try
{
int bytesRead = Connection.EndReceive(ar);
if (bytesRead >= 5)
{
_command = ConnetionRecvBuffer[1];
if (_command != 1 && _command != 3)
{
Logging.Debug("Unsupported CMD=" + _command);
Close();
}
else
{
int atyp = ConnetionRecvBuffer[3];

switch (atyp)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1);
break;
case 3: // domain name, length + str
int len = ConnetionRecvBuffer[4];
ReadAddress(len + 2);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
}
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}


private void ReadAddress(int bytesRemain)
{
Array.Copy(ConnetionRecvBuffer, 3, ConnetionRecvBuffer, 0, 2);

// Read the remain address bytes
Connection.BeginReceive(ConnetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain);
}

private void OnAddressFullyRead(IAsyncResult ar)
{
if (Closed) return;
try
{
int bytesRead = Connection.EndReceive(ar);
int bytesRemain = (int)ar.AsyncState;
if (bytesRead >= bytesRemain)
{
_firstPacketLength = bytesRead + 2;

int atyp = ConnetionRecvBuffer[0];

string dst_addr = "Unknown";
int dst_port = -1;
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
dst_addr = new IPAddress(ConnetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
dst_port = (ConnetionRecvBuffer[5] << 8) + ConnetionRecvBuffer[6];

break;
case 3: // domain name, length + str
int len = ConnetionRecvBuffer[1];
dst_addr = System.Text.Encoding.UTF8.GetString(ConnetionRecvBuffer, 2, len);
dst_port = (ConnetionRecvBuffer[len + 2] << 8) + ConnetionRecvBuffer[len + 3];

break;
case 4: // IPv6 address, 16 bytes
dst_addr = $"[{new IPAddress(ConnetionRecvBuffer.Skip(1).Take(16).ToArray())}]";
dst_port = (ConnetionRecvBuffer[17] << 8) + ConnetionRecvBuffer[18];

break;
}
if (Config.isVerboseLogging)
{
Logging.Info($"connect to {dst_addr}:{dst_port}");
}

var destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port);

if (_command == 1)
{
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
Connection.BeginSend(response, 0, response.Length, SocketFlags.None,
ResponseCallback, destEndPoint);
}
else if (_command == 3)
{
HandleUDPAssociate();
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.OnAddressFullyRead()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}

private void HandleUDPAssociate()
{
IPEndPoint endPoint = (IPEndPoint)Connection.LocalEndPoint;
byte[] address = endPoint.Address.GetAddressBytes();
int port = endPoint.Port;
byte[] response = new byte[4 + address.Length + 2];
response[0] = 5;
switch (endPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
response[3] = 1;
break;
case AddressFamily.InterNetworkV6:
response[3] = 4;
break;
}
address.CopyTo(response, 4);
response[response.Length - 1] = (byte)(port & 0xFF);
response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
Connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
}

private void ReadAll(IAsyncResult ar)
{
if (Closed) return;
try
{
if (ar.AsyncState != null)
{
Connection.EndSend(ar);
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
{
int bytesRead = Connection.EndReceive(ar);
if (bytesRead > 0)
{
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}

private void ResponseCallback(IAsyncResult ar)
{
try
{
Connection.EndSend(ar);
StartConnect((EndPoint) ar.AsyncState);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}

protected override void OnServerConnected(AsyncSession session)
{
BeginSendToServer(_firstPacketLength, session, FirstPackageSendCallback);
}

private void FirstPackageSendCallback(IAsyncResult ar)
{
if (Closed) return;
try
{
var session = EndSendToServer(ar);
StartPipe(session);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
}
}

+ 139
- 333
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -1,17 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Timers; using System.Timers;
using Shadowsocks.Controller.Strategy; using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption; using Shadowsocks.Encryption;
using Shadowsocks.ForwardProxy; using Shadowsocks.ForwardProxy;
using Shadowsocks.Model; using Shadowsocks.Model;
using Shadowsocks.Util.Sockets; using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Controller
namespace Shadowsocks.Controller.Service
{ {
class TCPRelay : Listener.Service class TCPRelay : Listener.Service
{ {
@@ -19,23 +17,39 @@ namespace Shadowsocks.Controller
private DateTime _lastSweepTime; private DateTime _lastSweepTime;
private Configuration _config; private Configuration _config;
public ISet<TCPHandler> Handlers { get; set; }
private readonly List<ITCPHandlerFactory> _factories = new List<ITCPHandlerFactory>();
public ISet<TCPHandler> Handlers { get; } = new HashSet<TCPHandler>();
public TCPRelay(ShadowsocksController controller, Configuration conf) public TCPRelay(ShadowsocksController controller, Configuration conf)
{ {
_controller = controller; _controller = controller;
_config = conf; _config = conf;
Handlers = new HashSet<TCPHandler>();
_lastSweepTime = DateTime.Now; _lastSweepTime = DateTime.Now;
_factories.Add(new Socks5HandlerFactory());
_factories.Add(new HttpHandlerHandlerFactory());
} }
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state) public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{ {
if (socket.ProtocolType != ProtocolType.Tcp
|| (length < 2 || firstPacket[0] != 5))
TCPHandler handler = null;
foreach (var factory in _factories)
{
if (factory.CanHandle(firstPacket, length))
{
handler = factory.NewHandler(_controller, _config, this, socket);
break;
}
}
if (handler == null)
{
return false; return false;
}
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
TCPHandler handler = new TCPHandler(_controller, _config, this, socket);
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); IList<TCPHandler> handlersToClose = new List<TCPHandler>();
lock (Handlers) lock (Handlers)
@@ -62,7 +76,7 @@ namespace Shadowsocks.Controller
* Then the handler will never release until the next Handle call. Sometimes it will * Then the handler will never release until the next Handle call. Sometimes it will
* cause odd problems (especially during memory profiling). * cause odd problems (especially during memory profiling).
*/ */
handler.Start(firstPacket, length);
handler.StartHandshake(firstPacket, length);
return true; return true;
} }
@@ -74,7 +88,7 @@ namespace Shadowsocks.Controller
{ {
handlersToClose.AddRange(Handlers); handlersToClose.AddRange(Handlers);
} }
handlersToClose.ForEach(h=>h.Close());
handlersToClose.ForEach(h => h.Close());
} }
public void UpdateInboundCounter(Server server, long n) public void UpdateInboundCounter(Server server, long n)
@@ -93,9 +107,21 @@ namespace Shadowsocks.Controller
} }
} }
class TCPHandler
interface ITCPHandlerFactory
{
bool CanHandle(byte[] firstPacket, int length);
TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket);
}
abstract class TCPHandler
{ {
class AsyncSession
public abstract void StartHandshake(byte[] firstPacket, int length);
protected abstract void OnServerConnected(AsyncSession session);
protected class AsyncSession
{ {
public IForwardProxy Remote { get; } public IForwardProxy Remote { get; }
@@ -105,7 +131,7 @@ namespace Shadowsocks.Controller
} }
} }
class AsyncSession<T> : AsyncSession
protected class AsyncSession<T> : AsyncSession
{ {
public T State { get; set; } public T State { get; set; }
@@ -114,7 +140,7 @@ namespace Shadowsocks.Controller
State = state; State = state;
} }
public AsyncSession(AsyncSession session, T state): base(session.Remote)
public AsyncSession(AsyncSession session, T state) : base(session.Remote)
{ {
State = state; State = state;
} }
@@ -130,39 +156,33 @@ namespace Shadowsocks.Controller
public DateTime lastActivity; public DateTime lastActivity;
private ShadowsocksController _controller;
private Configuration _config;
private TCPRelay _tcprelay;
private Socket _connection;
private IEncryptor _encryptor;
private Server _server;
private ShadowsocksController _controller;
protected Configuration Config { get; }
private TCPRelay _tcprelay;
protected Socket Connection { get; }
private Server _server;
private AsyncSession _currentRemoteSession; private AsyncSession _currentRemoteSession;
private bool _proxyConnected;
private bool _destConnected;
private bool _proxyConnected;
private bool _destConnected;
private byte _command;
private byte[] _firstPacket;
private int _firstPacketLength;
private int _totalRead = 0;
private int _totalWrite = 0;
private int _totalRead = 0;
private int _totalWrite = 0;
protected byte[] RemoteRecvBuffer { get; } = new byte[BufferSize];
private readonly byte[] _remoteSendBuffer = new byte[BufferSize];
protected byte[] ConnetionRecvBuffer { get; } = new byte[BufferSize];
private readonly byte[] _connetionSendBuffer = new byte[BufferSize];
private byte[] _remoteRecvBuffer = new byte[BufferSize];
private byte[] _remoteSendBuffer = new byte[BufferSize];
private byte[] _connetionRecvBuffer = new byte[BufferSize];
private byte[] _connetionSendBuffer = new byte[BufferSize];
private IEncryptor _encryptor;
private readonly object _encryptionLock = new object();
private readonly object _decryptionLock = new object();
private bool _connectionShutdown = false;
private bool _remoteShutdown = false;
private bool _closed = false;
// instance-based lock without static
private readonly object _encryptionLock = new object();
private readonly object _decryptionLock = new object();
private readonly object _closeConnLock = new object();
private bool _connectionShutdown = false;
private bool _remoteShutdown = false;
protected bool Closed { get; private set; }= false;
private readonly object _closeConnLock = new object();
private DateTime _startConnectTime; private DateTime _startConnectTime;
private DateTime _startReceivingTime; private DateTime _startReceivingTime;
@@ -170,21 +190,24 @@ namespace Shadowsocks.Controller
private EndPoint _destEndPoint = null; private EndPoint _destEndPoint = null;
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket, bool autoAppendHeader = true)
{ {
_controller = controller; _controller = controller;
_config = config;
Config = config;
_tcprelay = tcprelay; _tcprelay = tcprelay;
_connection = socket;
Connection = socket;
_proxyTimeout = config.proxy.proxyTimeout * 1000; _proxyTimeout = config.proxy.proxyTimeout * 1000;
_serverTimeout = config.GetCurrentServer().timeout * 1000; _serverTimeout = config.GetCurrentServer().timeout * 1000;
lastActivity = DateTime.Now; lastActivity = DateTime.Now;
_serverHeaderSent = !autoAppendHeader;
} }
public void CreateRemote()
private void CreateRemote()
{ {
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, _destEndPoint);
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)Connection.RemoteEndPoint, _destEndPoint);
if (server == null || server.server == "") if (server == null || server.server == "")
throw new ArgumentException("No server configured"); throw new ArgumentException("No server configured");
lock (_encryptionLock) lock (_encryptionLock)
@@ -197,13 +220,6 @@ namespace Shadowsocks.Controller
this._server = server; this._server = server;
} }
public void Start(byte[] firstPacket, int length)
{
_firstPacket = firstPacket;
_firstPacketLength = length;
HandshakeReceive();
}
private void CheckClose() private void CheckClose()
{ {
if (_connectionShutdown && _remoteShutdown) if (_connectionShutdown && _remoteShutdown)
@@ -214,8 +230,8 @@ namespace Shadowsocks.Controller
{ {
lock (_closeConnLock) lock (_closeConnLock)
{ {
if (_closed) return;
_closed = true;
if (Closed) return;
Closed = true;
} }
lock (_tcprelay.Handlers) lock (_tcprelay.Handlers)
{ {
@@ -223,8 +239,8 @@ namespace Shadowsocks.Controller
} }
try try
{ {
_connection.Shutdown(SocketShutdown.Both);
_connection.Close();
Connection.Shutdown(SocketShutdown.Both);
Connection.Close();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -249,245 +265,6 @@ namespace Shadowsocks.Controller
} }
} }
private void HandshakeReceive()
{
if (_closed) return;
try
{
int bytesRead = _firstPacketLength;
if (bytesRead > 1)
{
byte[] response = { 5, 0 };
if (_firstPacket[0] != 5)
{
// reject socks 4
response = new byte[] { 0, 91 };
Logging.Error("socks 5 protocol error");
}
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
}
else
Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void HandshakeSendCallback(IAsyncResult ar)
{
if (_closed) return;
try
{
_connection.EndSend(ar);
// +-----+-----+-------+------+----------+----------+
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +-----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +-----+-----+-------+------+----------+----------+
// Skip first 3 bytes, and read 2 more bytes to analysis the address.
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length.
// TODO validate
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + 2, SocketFlags.None,
new AsyncCallback(handshakeReceive2Callback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void handshakeReceive2Callback(IAsyncResult ar)
{
if (_closed) return;
try
{
int bytesRead = _connection.EndReceive(ar);
if (bytesRead >= 5)
{
_command = _connetionRecvBuffer[1];
if (_command != 1 && _command != 3)
{
Logging.Debug("Unsupported CMD=" + _command);
Close();
}
else
{
int atyp = _connetionRecvBuffer[3];
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1);
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[4];
ReadAddress(len + 2);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
}
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ReadAddress(int bytesRemain)
{
Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2);
// Read the remain address bytes
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain);
}
private void OnAddressFullyRead(IAsyncResult ar)
{
if (_closed) return;
try
{
int bytesRead = _connection.EndReceive(ar);
int bytesRemain = (int) ar.AsyncState;
if (bytesRead >= bytesRemain)
{
_firstPacketLength = bytesRead + 2;
int atyp = _connetionRecvBuffer[0];
string dst_addr = "Unknown";
int dst_port = -1;
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6];
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[1];
dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len);
dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3];
break;
case 4: // IPv6 address, 16 bytes
dst_addr = $"[{new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray())}]";
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
break;
}
if (_config.isVerboseLogging)
{
Logging.Info($"connect to {dst_addr}:{dst_port}");
}
_destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port);
if (_command == 1)
{
byte[] response = {5, 0, 0, 1, 0, 0, 0, 0, 0, 0};
_connection.BeginSend(response, 0, response.Length, SocketFlags.None,
new AsyncCallback(ResponseCallback), null);
}
else if (_command == 3)
{
HandleUDPAssociate();
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.OnAddressFullyRead()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void HandleUDPAssociate()
{
IPEndPoint endPoint = (IPEndPoint)_connection.LocalEndPoint;
byte[] address = endPoint.Address.GetAddressBytes();
int port = endPoint.Port;
byte[] response = new byte[4 + address.Length + 2];
response[0] = 5;
switch (endPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
response[3] = 1;
break;
case AddressFamily.InterNetworkV6:
response[3] = 4;
break;
}
address.CopyTo(response, 4);
response[response.Length - 1] = (byte)(port & 0xFF);
response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
}
private void ReadAll(IAsyncResult ar)
{
if (_closed) return;
try
{
if (ar.AsyncState != null)
{
_connection.EndSend(ar);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
{
int bytesRead = _connection.EndReceive(ar);
if (bytesRead > 0)
{
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ResponseCallback(IAsyncResult ar)
{
try
{
_connection.EndSend(ar);
StartConnect();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
// inner class // inner class
private class ProxyTimer : Timer private class ProxyTimer : Timer
{ {
@@ -509,18 +286,20 @@ namespace Shadowsocks.Controller
public ServerTimer(int p) : base(p) { } public ServerTimer(int p) : base(p) { }
} }
private void StartConnect()
protected void StartConnect(EndPoint target)
{ {
try try
{ {
_destEndPoint = target;
CreateRemote(); CreateRemote();
// Setting up proxy // Setting up proxy
IForwardProxy remote; IForwardProxy remote;
EndPoint proxyEP; EndPoint proxyEP;
if (_config.proxy.useProxy)
if (Config.proxy.useProxy)
{ {
switch (_config.proxy.proxyType)
switch (Config.proxy.proxyType)
{ {
case ProxyConfig.PROXY_SOCKS5: case ProxyConfig.PROXY_SOCKS5:
remote = new Socks5Proxy(); remote = new Socks5Proxy();
@@ -531,7 +310,7 @@ namespace Shadowsocks.Controller
default: default:
throw new NotSupportedException("Unknown forward proxy."); throw new NotSupportedException("Unknown forward proxy.");
} }
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort);
proxyEP = SocketUtil.GetEndPoint(Config.proxy.proxyServer, Config.proxy.proxyPort);
} }
else else
{ {
@@ -554,7 +333,7 @@ namespace Shadowsocks.Controller
_proxyConnected = false; _proxyConnected = false;
// Connect to the proxy server. // Connect to the proxy server.
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), new AsyncSession<ProxyTimer>(remote, proxyTimer));
remote.BeginConnectProxy(proxyEP, ProxyConnectCallback, new AsyncSession<ProxyTimer>(remote, proxyTimer));
} }
catch (Exception e) catch (Exception e)
{ {
@@ -563,15 +342,16 @@ namespace Shadowsocks.Controller
} }
} }
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{ {
var timer = (ProxyTimer) sender;
var timer = (ProxyTimer)sender;
timer.Elapsed -= proxyConnectTimer_Elapsed; timer.Elapsed -= proxyConnectTimer_Elapsed;
timer.Enabled = false; timer.Enabled = false;
timer.Dispose(); timer.Dispose();
if (_proxyConnected || _destConnected || _closed)
if (_proxyConnected || _destConnected || Closed)
{ {
return; return;
} }
@@ -585,13 +365,13 @@ namespace Shadowsocks.Controller
private void ProxyConnectCallback(IAsyncResult ar) private void ProxyConnectCallback(IAsyncResult ar)
{ {
Server server = null; Server server = null;
if (_closed)
if (Closed)
{ {
return; return;
} }
try try
{ {
var session = (AsyncSession<ProxyTimer>) ar.AsyncState;
var session = (AsyncSession<ProxyTimer>)ar.AsyncState;
ProxyTimer timer = session.State; ProxyTimer timer = session.State;
var destEndPoint = timer.DestEndPoint; var destEndPoint = timer.DestEndPoint;
server = timer.Server; server = timer.Server;
@@ -606,7 +386,7 @@ namespace Shadowsocks.Controller
_proxyConnected = true; _proxyConnected = true;
if (_config.isVerboseLogging)
if (Config.isVerboseLogging)
{ {
if (!(remote is DirectConnect)) if (!(remote is DirectConnect))
{ {
@@ -621,10 +401,10 @@ namespace Shadowsocks.Controller
connectTimer.Enabled = true; connectTimer.Enabled = true;
connectTimer.Session = session; connectTimer.Session = session;
connectTimer.Server = server; connectTimer.Server = server;
_destConnected = false; _destConnected = false;
// Connect to the remote endpoint. // Connect to the remote endpoint.
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), new AsyncSession<ServerTimer>(session, connectTimer));
remote.BeginConnectDest(destEndPoint, ConnectCallback, new AsyncSession<ServerTimer>(session, connectTimer));
} }
catch (ArgumentException) catch (ArgumentException)
{ {
@@ -643,7 +423,7 @@ namespace Shadowsocks.Controller
timer.Enabled = false; timer.Enabled = false;
timer.Dispose(); timer.Dispose();
if (_destConnected || _closed)
if (_destConnected || Closed)
{ {
return; return;
} }
@@ -659,10 +439,10 @@ namespace Shadowsocks.Controller
private void ConnectCallback(IAsyncResult ar) private void ConnectCallback(IAsyncResult ar)
{ {
if (_closed) return;
if (Closed) return;
try try
{ {
var session = (AsyncSession<ServerTimer>) ar.AsyncState;
var session = (AsyncSession<ServerTimer>)ar.AsyncState;
ServerTimer timer = session.State; ServerTimer timer = session.State;
_server = timer.Server; _server = timer.Server;
timer.Elapsed -= destConnectTimer_Elapsed; timer.Elapsed -= destConnectTimer_Elapsed;
@@ -672,10 +452,10 @@ namespace Shadowsocks.Controller
var remote = session.Remote; var remote = session.Remote;
// Complete the connection. // Complete the connection.
remote.EndConnectDest(ar); remote.EndConnectDest(ar);
_destConnected = true; _destConnected = true;
if (_config.isVerboseLogging)
if (Config.isVerboseLogging)
{ {
Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}"); Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}");
} }
@@ -685,7 +465,7 @@ namespace Shadowsocks.Controller
strategy?.UpdateLatency(_server, latency); strategy?.UpdateLatency(_server, latency);
_tcprelay.UpdateLatency(_server, latency); _tcprelay.UpdateLatency(_server, latency);
StartPipe(session);
OnServerConnected(session);
} }
catch (ArgumentException) catch (ArgumentException)
{ {
@@ -702,14 +482,14 @@ namespace Shadowsocks.Controller
} }
} }
private void StartPipe(AsyncSession session)
protected void StartPipe(AsyncSession session)
{ {
if (_closed) return;
if (Closed) return;
try try
{ {
_startReceivingTime = DateTime.Now; _startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
SendToServer(_firstPacketLength, session);
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session);
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -720,10 +500,10 @@ namespace Shadowsocks.Controller
private void PipeRemoteReceiveCallback(IAsyncResult ar) private void PipeRemoteReceiveCallback(IAsyncResult ar)
{ {
if (_closed) return;
if (Closed) return;
try try
{ {
var session = (AsyncSession) ar.AsyncState;
var session = (AsyncSession)ar.AsyncState;
int bytesRead = session.Remote.EndReceive(ar); int bytesRead = session.Remote.EndReceive(ar);
_totalRead += bytesRead; _totalRead += bytesRead;
_tcprelay.UpdateInboundCounter(_server, bytesRead); _tcprelay.UpdateInboundCounter(_server, bytesRead);
@@ -733,15 +513,15 @@ namespace Shadowsocks.Controller
int bytesToSend; int bytesToSend;
lock (_decryptionLock) lock (_decryptionLock)
{ {
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
_encryptor.Decrypt(RemoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
} }
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
Connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, PipeConnectionSendCallback, session);
IStrategy strategy = _controller.GetCurrentStrategy(); IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastRead(_server); strategy?.UpdateLastRead(_server);
} }
else else
{ {
_connection.Shutdown(SocketShutdown.Send);
Connection.Shutdown(SocketShutdown.Send);
_connectionShutdown = true; _connectionShutdown = true;
CheckClose(); CheckClose();
} }
@@ -755,12 +535,12 @@ namespace Shadowsocks.Controller
private void PipeConnectionReceiveCallback(IAsyncResult ar) private void PipeConnectionReceiveCallback(IAsyncResult ar)
{ {
if (_closed) return;
if (Closed) return;
try try
{ {
int bytesRead = _connection.EndReceive(ar);
int bytesRead = Connection.EndReceive(ar);
var session = (AsyncSession) ar.AsyncState;
var session = (AsyncSession)ar.AsyncState;
var remote = session.Remote; var remote = session.Remote;
if (bytesRead > 0) if (bytesRead > 0)
@@ -783,27 +563,53 @@ namespace Shadowsocks.Controller
private void SendToServer(int length, AsyncSession session) private void SendToServer(int length, AsyncSession session)
{ {
BeginSendToServer(length, session, PipeRemoteSendCallback);
}
private bool _serverHeaderSent;
protected void BeginSendToServer(int length, AsyncSession session, AsyncCallback callback)
{
if (!_serverHeaderSent)
{
_serverHeaderSent = true;
// Append socks5 header
int len = Socks5Util.HeaderAddrLength(_destEndPoint);
Array.Copy(ConnetionRecvBuffer, 0, ConnetionRecvBuffer, len, length);
Socks5Util.FillHeaderAddr(ConnetionRecvBuffer, 0, _destEndPoint);
length += len;
}
_totalWrite += length; _totalWrite += length;
int bytesToSend; int bytesToSend;
lock (_encryptionLock) lock (_encryptionLock)
{ {
_encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
_encryptor.Encrypt(ConnetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
} }
_tcprelay.UpdateOutboundCounter(_server, bytesToSend); _tcprelay.UpdateOutboundCounter(_server, bytesToSend);
_startSendingTime = DateTime.Now; _startSendingTime = DateTime.Now;
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, callback, session);
IStrategy strategy = _controller.GetCurrentStrategy(); IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(_server); strategy?.UpdateLastWrite(_server);
} }
protected AsyncSession EndSendToServer(IAsyncResult ar)
{
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
return session;
}
private void PipeRemoteSendCallback(IAsyncResult ar) private void PipeRemoteSendCallback(IAsyncResult ar)
{ {
if (_closed) return;
if (Closed) return;
try try
{ {
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
var session = EndSendToServer(ar);
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -817,8 +623,8 @@ namespace Shadowsocks.Controller
try try
{ {
var session = (AsyncSession)ar.AsyncState; var session = (AsyncSession)ar.AsyncState;
_connection.EndSend(ar);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
Connection.EndSend(ar);
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session);
} }
catch (Exception e) catch (Exception e)
{ {


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

@@ -459,10 +459,10 @@ namespace Shadowsocks.Controller
TCPRelay tcpRelay = new TCPRelay(this, _config); TCPRelay tcpRelay = new TCPRelay(this, _config);
UDPRelay udpRelay = new UDPRelay(this); UDPRelay udpRelay = new UDPRelay(this);
List<Listener.IService> services = new List<Listener.IService>(); List<Listener.IService> services = new List<Listener.IService>();
services.Add(_pacServer);
services.Add(tcpRelay); services.Add(tcpRelay);
services.Add(udpRelay); services.Add(udpRelay);
services.Add(_pacServer);
services.Add(new Http2Socks5(_config.localPort));
//services.Add(new Http2Socks5(_config.localPort));
_listener = new Listener(services); _listener = new Listener(services);
_listener.Start(_config); _listener.Start(_config);
} }


+ 3
- 40
shadowsocks-csharp/ForwardProxy/Socks5Proxy.cs View File

@@ -73,52 +73,15 @@ namespace Shadowsocks.ForwardProxy
{ {
DestEndPoint = destEndPoint; DestEndPoint = destEndPoint;


byte[] request = null;
byte atyp = 0;
int port;
int len = Socks5Util.HeaderAddrLength(destEndPoint);


var dep = destEndPoint as DnsEndPoint;
if (dep != null)
{
// is a domain name, we will leave it to server

atyp = 3; // DOMAINNAME
var enc = Encoding.UTF8;
var hostByteCount = enc.GetByteCount(dep.Host);

request = new byte[4 + 1/*length byte*/ + hostByteCount + 2];
request[4] = (byte)hostByteCount;
enc.GetBytes(dep.Host, 0, dep.Host.Length, request, 5);

port = dep.Port;
}
else
{
switch (DestEndPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
request = new byte[4 + 4 + 2];
atyp = 1; // IP V4 address
break;
case AddressFamily.InterNetworkV6:
request = new byte[4 + 16 + 2];
atyp = 4; // IP V6 address
break;
default:
throw new Exception(I18N.GetString("Proxy request failed"));
}
port = ((IPEndPoint) DestEndPoint).Port;
var addr = ((IPEndPoint)DestEndPoint).Address.GetAddressBytes();
Array.Copy(addr, 0, request, 4, request.Length - 4 - 2);
}
byte[] request = new byte[len + 3];
Socks5Util.FillHeaderAddr(request, 3, destEndPoint);


// 构造request包剩余部分 // 构造request包剩余部分
request[0] = 5; request[0] = 5;
request[1] = 1; request[1] = 1;
request[2] = 0; request[2] = 0;
request[3] = atyp;
request[request.Length - 2] = (byte) ((port >> 8) & 0xff);
request[request.Length - 1] = (byte) (port & 0xff);


var st = new Socks5State(); var st = new Socks5State();
st.Callback = callback; st.Callback = callback;


+ 85
- 0
shadowsocks-csharp/Util/Sockets/Socks5Util.cs View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Shadowsocks.Controller;

namespace Shadowsocks.Util.Sockets
{
public static class Socks5Util
{
public static int HeaderAddrLength(EndPoint addrEp)
{
var dep = addrEp as DnsEndPoint;
if (dep != null)
{
var enc = Encoding.UTF8;
var hostByteCount = enc.GetByteCount(dep.Host);

return 1 + 1 /*length byte*/+ hostByteCount + 2;
}

switch (addrEp.AddressFamily)
{
case AddressFamily.InterNetwork:
return 1 + 4 + 2;
case AddressFamily.InterNetworkV6:
return 1 + 16 + 2;
default:
throw new Exception(I18N.GetString("Proxy request failed"));
}
}

public static int FillHeaderAddr(byte[] buffer, int offset, EndPoint addrEp)
{
byte atyp;
int port;
int len;

var dep = addrEp as DnsEndPoint;
if (dep != null)
{
// is a domain name, we will leave it to server

atyp = 3; // DOMAINNAME
var enc = Encoding.UTF8;
var hostByteCount = enc.GetByteCount(dep.Host);

len = 1 + 1 /*length byte*/+ hostByteCount + 2;

buffer[offset + 1] = (byte)hostByteCount;
enc.GetBytes(dep.Host, 0, dep.Host.Length, buffer, offset + 2);

port = dep.Port;
}
else
{
switch (addrEp.AddressFamily)
{
case AddressFamily.InterNetwork:
len = 1 + 4 + 2;
atyp = 1; // IP V4 address
break;
case AddressFamily.InterNetworkV6:
len = 1 + 16 + 2;
atyp = 4; // IP V6 address
break;
default:
throw new Exception(I18N.GetString("Proxy request failed"));
}
port = ((IPEndPoint)addrEp).Port;
var addr = ((IPEndPoint)addrEp).Address.GetAddressBytes();
Array.Copy(addr, 0, buffer, offset + 1, len - 1 - 2);
}

buffer[offset] = atyp;
buffer[offset + len - 2] = (byte)((port >> 8) & 0xff);
buffer[offset + len - 1] = (byte)(port & 0xff);

return len;
}
}
}

+ 4
- 2
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -140,7 +140,9 @@
<Compile Include="3rd\zxing\ResultPoint.cs" /> <Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> <Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.cs" /> <Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Controller\Service\Http2Socks5.cs" />
<Compile Include="Controller\Service\HttpHandler.cs" />
<Compile Include="Controller\Service\Socks5Handler.cs" />
<Compile Include="Controller\Service\TCPRelay.cs" />
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" /> <Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" />
<Compile Include="Model\HotKeyConfig.cs" /> <Compile Include="Model\HotKeyConfig.cs" />
<Compile Include="Model\ProxyConfig.cs" /> <Compile Include="Model\ProxyConfig.cs" />
@@ -193,6 +195,7 @@
<Compile Include="Util\Sockets\ByteSearch.cs" /> <Compile Include="Util\Sockets\ByteSearch.cs" />
<Compile Include="Util\Sockets\LineReader.cs" /> <Compile Include="Util\Sockets\LineReader.cs" />
<Compile Include="Util\Sockets\SocketUtil.cs" /> <Compile Include="Util\Sockets\SocketUtil.cs" />
<Compile Include="Util\Sockets\Socks5Util.cs" />
<Compile Include="Util\Sockets\WrappedSocket.cs" /> <Compile Include="Util\Sockets\WrappedSocket.cs" />
<Compile Include="Util\SystemProxy\INTERNET_OPTION.cs" /> <Compile Include="Util\SystemProxy\INTERNET_OPTION.cs" />
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" /> <Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" />
@@ -209,7 +212,6 @@
<Compile Include="View\ConfigForm.Designer.cs"> <Compile Include="View\ConfigForm.Designer.cs">
<DependentUpon>ConfigForm.cs</DependentUpon> <DependentUpon>ConfigForm.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Controller\Service\TCPRelay.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Controller\ShadowsocksController.cs" /> <Compile Include="Controller\ShadowsocksController.cs" />


Loading…
Cancel
Save