Browse Source

Parse dest address before connect to SS server.

This gives us the possibility to chose SS server based on dest address.
tags/3.3.6
noisyfox 7 years ago
parent
commit
4b49431800
7 changed files with 130 additions and 64 deletions
  1. +122
    -57
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  2. +1
    -1
      shadowsocks-csharp/Controller/Service/UDPRelay.cs
  3. +2
    -2
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  4. +1
    -1
      shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs
  5. +2
    -1
      shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs
  6. +1
    -1
      shadowsocks-csharp/Controller/Strategy/IStrategy.cs
  7. +1
    -1
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs

+ 122
- 57
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -168,6 +168,8 @@ namespace Shadowsocks.Controller
private DateTime _startReceivingTime;
private DateTime _startSendingTime;
private EndPoint _destEndPoint = null;
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
{
_controller = controller;
@@ -182,7 +184,7 @@ namespace Shadowsocks.Controller
public void CreateRemote()
{
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint);
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, _destEndPoint);
if (server == null || server.server == "")
throw new ArgumentException("No server configured");
lock (_encryptionLock)
@@ -286,9 +288,11 @@ namespace Shadowsocks.Controller
// +-----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +-----+-----+-------+------+----------+----------+
// Skip first 3 bytes
// 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, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null);
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + 2, SocketFlags.None,
new AsyncCallback(handshakeReceive2Callback), null);
}
catch (Exception e)
{
@@ -303,20 +307,113 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = _connection.EndReceive(ar);
if (bytesRead >= 3)
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);
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.handshakeReceive2Callback()");
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.OnAddressFullyRead()");
Close();
}
}
@@ -612,8 +709,7 @@ namespace Shadowsocks.Controller
{
_startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
new AsyncSession<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */);
SendToServer(_firstPacketLength, session);
}
catch (Exception e)
{
@@ -663,59 +759,13 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = _connection.EndReceive(ar);
_totalWrite += bytesRead;
var session = (AsyncSession<bool>) ar.AsyncState;
var session = (AsyncSession) ar.AsyncState;
var remote = session.Remote;
if (bytesRead > 0)
{
/*
* Only the first packet contains the socks5 header, it doesn't make sense to parse every packets.
* Also it's unnecessary to parse these data if we turn off the VerboseLogging.
*/
if (session.State && _config.isVerboseLogging)
{
int atyp = _connetionRecvBuffer[0];
string dst_addr;
int dst_port;
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];
Logging.Info($"connect to {dst_addr}:{dst_port}");
session.State = false;
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];
Logging.Info($"connect to {dst_addr}:{dst_port}");
session.State = false;
break;
case 4: // IPv6 address, 16 bytes
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray()).ToString();
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
Logging.Info($"connect to [{dst_addr}]:{dst_port}");
session.State = false;
break;
}
}
int bytesToSend;
lock (_encryptionLock)
{
_encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend);
}
_tcprelay.UpdateOutboundCounter(_server, bytesToSend);
_startSendingTime = DateTime.Now;
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(_server);
SendToServer(bytesRead, session);
}
else
{
@@ -731,6 +781,21 @@ namespace Shadowsocks.Controller
}
}
private void SendToServer(int length, AsyncSession session)
{
_totalWrite += length;
int bytesToSend;
lock (_encryptionLock)
{
_encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
}
_tcprelay.UpdateOutboundCounter(_server, bytesToSend);
_startSendingTime = DateTime.Now;
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(_server);
}
private void PipeRemoteSendCallback(IAsyncResult ar)
{
if (_closed) return;


+ 1
- 1
shadowsocks-csharp/Controller/Service/UDPRelay.cs View File

@@ -39,7 +39,7 @@ namespace Shadowsocks.Controller
UDPHandler handler = _cache.get(remoteEndPoint);
if (handler == null)
{
handler = new UDPHandler(socket, _controller.GetAServer(IStrategyCallerType.UDP, remoteEndPoint), remoteEndPoint);
handler = new UDPHandler(socket, _controller.GetAServer(IStrategyCallerType.UDP, remoteEndPoint, null/*TODO: fix this*/), remoteEndPoint);
_cache.add(remoteEndPoint, handler);
}
handler.Send(firstPacket, length);


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

@@ -139,12 +139,12 @@ namespace Shadowsocks.Controller
return null;
}
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint)
{
IStrategy strategy = GetCurrentStrategy();
if (strategy != null)
{
return strategy.GetAServer(type, localIPEndPoint);
return strategy.GetAServer(type, localIPEndPoint, destEndPoint);
}
if (_config.index < 0)
{


+ 1
- 1
shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs View File

@@ -33,7 +33,7 @@ namespace Shadowsocks.Controller.Strategy
// do nothing
}
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint)
{
var configs = _controller.GetCurrentConfiguration().configs;
int index;


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

@@ -1,6 +1,7 @@
using Shadowsocks.Model;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
namespace Shadowsocks.Controller.Strategy
@@ -78,7 +79,7 @@ namespace Shadowsocks.Controller.Strategy
ChooseNewServer();
}
public Server GetAServer(IStrategyCallerType type, System.Net.IPEndPoint localIPEndPoint)
public Server GetAServer(IStrategyCallerType type, System.Net.IPEndPoint localIPEndPoint, EndPoint destEndPoint)
{
if (type == IStrategyCallerType.TCP)
{


+ 1
- 1
shadowsocks-csharp/Controller/Strategy/IStrategy.cs View File

@@ -31,7 +31,7 @@ namespace Shadowsocks.Controller.Strategy
/*
* Get a new server to use in TCPRelay or UDPRelay
*/
Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint);
Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint);
/*
* TCPRelay will call this when latency of a server detected


+ 1
- 1
shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs View File

@@ -127,7 +127,7 @@ namespace Shadowsocks.Controller.Strategy

public string Name => I18N.GetString("Choose by statistics");

public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint)
{
if (_currentServer == null)
{


Loading…
Cancel
Save