Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60a5572808 | ||
|
|
877644609b | ||
|
|
54335d4889 | ||
|
|
08f12b3b24 | ||
|
|
1c8f4168cf | ||
|
|
43672a8bda |
@@ -1,3 +1,8 @@
|
|||||||
|
4.0.1 2017-04-08
|
||||||
|
- Fix UDP relay
|
||||||
|
- Allow to add multiple servers via Shadowsocks URL
|
||||||
|
- Bug fixes and improvements
|
||||||
|
|
||||||
4.0 2017-04-04
|
4.0 2017-04-04
|
||||||
- Add AEAD ciphers support, removed OTA
|
- Add AEAD ciphers support, removed OTA
|
||||||
- I18N: add Japanese support, update Traditional Chinese strings
|
- I18N: add Japanese support, update Traditional Chinese strings
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ namespace Shadowsocks.Controller
|
|||||||
{
|
{
|
||||||
class PortForwarder : Listener.Service
|
class PortForwarder : Listener.Service
|
||||||
{
|
{
|
||||||
int _targetPort;
|
private readonly int _targetPort;
|
||||||
|
|
||||||
public PortForwarder(int targetPort)
|
public PortForwarder(int targetPort)
|
||||||
{
|
{
|
||||||
this._targetPort = targetPort;
|
_targetPort = targetPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||||
@@ -20,11 +20,11 @@ namespace Shadowsocks.Controller
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
new Handler().Start(firstPacket, length, socket, this._targetPort);
|
new Handler().Start(firstPacket, length, socket, _targetPort);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Handler
|
private class Handler
|
||||||
{
|
{
|
||||||
private byte[] _firstPacket;
|
private byte[] _firstPacket;
|
||||||
private int _firstPacketLength;
|
private int _firstPacketLength;
|
||||||
@@ -33,7 +33,7 @@ namespace Shadowsocks.Controller
|
|||||||
private bool _closed = false;
|
private bool _closed = false;
|
||||||
private bool _localShutdown = false;
|
private bool _localShutdown = false;
|
||||||
private bool _remoteShutdown = false;
|
private bool _remoteShutdown = false;
|
||||||
public const int RecvSize = 16384;
|
private const int RecvSize = 2048;
|
||||||
// remote receive buffer
|
// remote receive buffer
|
||||||
private byte[] remoteRecvBuffer = new byte[RecvSize];
|
private byte[] remoteRecvBuffer = new byte[RecvSize];
|
||||||
// connection receive buffer
|
// connection receive buffer
|
||||||
@@ -44,9 +44,9 @@ namespace Shadowsocks.Controller
|
|||||||
|
|
||||||
public void Start(byte[] firstPacket, int length, Socket socket, int targetPort)
|
public void Start(byte[] firstPacket, int length, Socket socket, int targetPort)
|
||||||
{
|
{
|
||||||
this._firstPacket = firstPacket;
|
_firstPacket = firstPacket;
|
||||||
this._firstPacketLength = length;
|
_firstPacketLength = length;
|
||||||
this._local = socket;
|
_local = socket;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EndPoint remoteEP = SocketUtil.GetEndPoint("127.0.0.1", targetPort);
|
EndPoint remoteEP = SocketUtil.GetEndPoint("127.0.0.1", targetPort);
|
||||||
@@ -58,7 +58,7 @@ namespace Shadowsocks.Controller
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.LogUsefulException(e);
|
Logging.LogUsefulException(e);
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ namespace Shadowsocks.Controller
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.LogUsefulException(e);
|
Logging.LogUsefulException(e);
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,12 +89,12 @@ namespace Shadowsocks.Controller
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_remote.BeginSend(_firstPacket, 0, _firstPacketLength, 0, new AsyncCallback(StartPipe), null);
|
_remote.BeginSend(_firstPacket, 0, _firstPacketLength, 0, StartPipe, null);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.LogUsefulException(e);
|
Logging.LogUsefulException(e);
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,14 +108,14 @@ namespace Shadowsocks.Controller
|
|||||||
{
|
{
|
||||||
_remote.EndSend(ar);
|
_remote.EndSend(ar);
|
||||||
_remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
|
_remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
|
||||||
new AsyncCallback(PipeRemoteReceiveCallback), null);
|
PipeRemoteReceiveCallback, null);
|
||||||
_local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
|
_local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
|
||||||
new AsyncCallback(PipeConnectionReceiveCallback), null);
|
PipeConnectionReceiveCallback, null);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.LogUsefulException(e);
|
Logging.LogUsefulException(e);
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,10 +128,9 @@ namespace Shadowsocks.Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
int bytesRead = _remote.EndReceive(ar);
|
int bytesRead = _remote.EndReceive(ar);
|
||||||
|
|
||||||
if (bytesRead > 0)
|
if (bytesRead > 0)
|
||||||
{
|
{
|
||||||
_local.BeginSend(remoteRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeConnectionSendCallback), null);
|
_local.BeginSend(remoteRecvBuffer, 0, bytesRead, 0, PipeConnectionSendCallback, null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -143,7 +142,7 @@ namespace Shadowsocks.Controller
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.LogUsefulException(e);
|
Logging.LogUsefulException(e);
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,10 +155,9 @@ namespace Shadowsocks.Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
int bytesRead = _local.EndReceive(ar);
|
int bytesRead = _local.EndReceive(ar);
|
||||||
|
|
||||||
if (bytesRead > 0)
|
if (bytesRead > 0)
|
||||||
{
|
{
|
||||||
_remote.BeginSend(connetionRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeRemoteSendCallback), null);
|
_remote.BeginSend(connetionRecvBuffer, 0, bytesRead, 0, PipeRemoteSendCallback, null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -171,7 +169,7 @@ namespace Shadowsocks.Controller
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.LogUsefulException(e);
|
Logging.LogUsefulException(e);
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,13 +182,13 @@ namespace Shadowsocks.Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_remote.EndSend(ar);
|
_remote.EndSend(ar);
|
||||||
_local.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0,
|
_local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
|
||||||
new AsyncCallback(PipeConnectionReceiveCallback), null);
|
PipeConnectionReceiveCallback, null);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.LogUsefulException(e);
|
Logging.LogUsefulException(e);
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,13 +201,13 @@ namespace Shadowsocks.Controller
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_local.EndSend(ar);
|
_local.EndSend(ar);
|
||||||
_remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0,
|
_remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
|
||||||
new AsyncCallback(PipeRemoteReceiveCallback), null);
|
PipeRemoteReceiveCallback, null);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.LogUsefulException(e);
|
Logging.LogUsefulException(e);
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +215,7 @@ namespace Shadowsocks.Controller
|
|||||||
{
|
{
|
||||||
if (_localShutdown && _remoteShutdown)
|
if (_localShutdown && _remoteShutdown)
|
||||||
{
|
{
|
||||||
this.Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace Shadowsocks.Controller
|
|||||||
private Socket _remote;
|
private Socket _remote;
|
||||||
|
|
||||||
private Server _server;
|
private Server _server;
|
||||||
private byte[] _buffer = new byte[1500];
|
private byte[] _buffer = new byte[65536];
|
||||||
|
|
||||||
private IPEndPoint _localEndPoint;
|
private IPEndPoint _localEndPoint;
|
||||||
private IPEndPoint _remoteEndPoint;
|
private IPEndPoint _remoteEndPoint;
|
||||||
@@ -81,7 +81,7 @@ namespace Shadowsocks.Controller
|
|||||||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
|
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
|
||||||
byte[] dataIn = new byte[length - 3];
|
byte[] dataIn = new byte[length - 3];
|
||||||
Array.Copy(data, 3, dataIn, 0, length - 3);
|
Array.Copy(data, 3, dataIn, 0, length - 3);
|
||||||
byte[] dataOut = new byte[length - 3 + 16];
|
byte[] dataOut = new byte[65536]; // enough space for AEAD ciphers
|
||||||
int outlen;
|
int outlen;
|
||||||
encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen);
|
encryptor.EncryptUDP(dataIn, length - 3, dataOut, out outlen);
|
||||||
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
|
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Shadowsocks.Controller
|
|||||||
public string LatestVersionLocalName;
|
public string LatestVersionLocalName;
|
||||||
public event EventHandler CheckUpdateCompleted;
|
public event EventHandler CheckUpdateCompleted;
|
||||||
|
|
||||||
public const string Version = "4.0";
|
public const string Version = "4.0.1";
|
||||||
|
|
||||||
private class CheckUpdateTimer : System.Timers.Timer
|
private class CheckUpdateTimer : System.Timers.Timer
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -163,8 +163,13 @@ namespace Shadowsocks.Controller
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var server = new Server(ssURL);
|
if (ssURL.IsNullOrEmpty() || ssURL.IsWhiteSpace()) return false;
|
||||||
_config.configs.Add(server);
|
var servers = Server.GetServers(ssURL);
|
||||||
|
if (servers == null || servers.Count == 0) return false;
|
||||||
|
foreach (var server in servers)
|
||||||
|
{
|
||||||
|
_config.configs.Add(server);
|
||||||
|
}
|
||||||
_config.index = _config.configs.Count - 1;
|
_config.index = _config.configs.Count - 1;
|
||||||
SaveConfig(_config);
|
SaveConfig(_config);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Shadowsocks.Encryption.AEAD
|
|||||||
private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info);
|
private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info);
|
||||||
|
|
||||||
// for UDP only
|
// for UDP only
|
||||||
protected static byte[] _udpTmpBuf = new byte[MAX_INPUT_SIZE];
|
protected static byte[] _udpTmpBuf = new byte[65536];
|
||||||
|
|
||||||
// every connection should create its own buffer
|
// every connection should create its own buffer
|
||||||
private CircularBuffer<byte> _encCircularBuffer = new CircularBuffer<byte>(MAX_INPUT_SIZE * 2, false);
|
private CircularBuffer<byte> _encCircularBuffer = new CircularBuffer<byte>(MAX_INPUT_SIZE * 2, false);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Shadowsocks.Encryption.Stream
|
|||||||
: EncryptorBase
|
: EncryptorBase
|
||||||
{
|
{
|
||||||
// for UDP only
|
// for UDP only
|
||||||
protected static byte[] _udpTmpBuf = new byte[MAX_INPUT_SIZE];
|
protected static byte[] _udpTmpBuf = new byte[65536];
|
||||||
|
|
||||||
// every connection should create its own buffer
|
// every connection should create its own buffer
|
||||||
private CircularBuffer<byte> _encCircularBuffer = new CircularBuffer<byte>(TCPHandler.BufferSize * 2, false);
|
private CircularBuffer<byte> _encCircularBuffer = new CircularBuffer<byte>(TCPHandler.BufferSize * 2, false);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
@@ -10,9 +11,8 @@ namespace Shadowsocks.Model
|
|||||||
public class Server
|
public class Server
|
||||||
{
|
{
|
||||||
public static readonly Regex
|
public static readonly Regex
|
||||||
UrlFinder = new Regex("^(?i)ss://([A-Za-z0-9+-/=_]+)(#(.+))?$", RegexOptions.IgnoreCase),
|
UrlFinder = new Regex(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase),
|
||||||
DetailsParser = new Regex("^((?<method>.+?):(?<password>.*)@(?<hostname>.+?)" +
|
DetailsParser = new Regex(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase);
|
||||||
":(?<port>\\d+?))$", RegexOptions.IgnoreCase);
|
|
||||||
|
|
||||||
private const int DefaultServerTimeoutSec = 5;
|
private const int DefaultServerTimeoutSec = 5;
|
||||||
public const int MaxServerTimeoutSec = 20;
|
public const int MaxServerTimeoutSec = 20;
|
||||||
@@ -43,9 +43,10 @@ namespace Shadowsocks.Model
|
|||||||
}
|
}
|
||||||
string serverStr;
|
string serverStr;
|
||||||
// CheckHostName() won't do a real DNS lookup
|
// CheckHostName() won't do a real DNS lookup
|
||||||
var hostType = Uri.CheckHostName( server );
|
var hostType = Uri.CheckHostName(server);
|
||||||
|
|
||||||
switch ( hostType ) {
|
switch (hostType)
|
||||||
|
{
|
||||||
case UriHostNameType.IPv6:
|
case UriHostNameType.IPv6:
|
||||||
serverStr = $"[{server}]:{server_port}";
|
serverStr = $"[{server}]:{server_port}";
|
||||||
break;
|
break;
|
||||||
@@ -69,20 +70,32 @@ namespace Shadowsocks.Model
|
|||||||
timeout = DefaultServerTimeoutSec;
|
timeout = DefaultServerTimeoutSec;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Server(string ssURL) : this()
|
public static List<Server> GetServers(string ssURL)
|
||||||
{
|
{
|
||||||
var match = UrlFinder.Match(ssURL);
|
var matches = UrlFinder.Matches(ssURL);
|
||||||
if (!match.Success) throw new FormatException();
|
if (matches.Count <= 0) return null;
|
||||||
var base64 = match.Groups[1].Value;
|
List<Server> servers = new List<Server>();
|
||||||
var tag = match.Groups[3].Value;
|
foreach (Match match in matches)
|
||||||
if (!tag.IsNullOrEmpty())
|
{
|
||||||
remarks = HttpUtility.UrlDecode(tag, Encoding.UTF8);
|
Server tmp = new Server();
|
||||||
match = DetailsParser.Match(Encoding.UTF8.GetString(Convert.FromBase64String(
|
var base64 = match.Groups["base64"].Value;
|
||||||
base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '='))));
|
var tag = match.Groups["tag"].Value;
|
||||||
method = match.Groups["method"].Value;
|
if (!tag.IsNullOrEmpty())
|
||||||
password = match.Groups["password"].Value;
|
{
|
||||||
server = match.Groups["hostname"].Value;
|
tmp.remarks = HttpUtility.UrlDecode(tag, Encoding.UTF8);
|
||||||
server_port = int.Parse(match.Groups["port"].Value);
|
}
|
||||||
|
Match details = DetailsParser.Match(Encoding.UTF8.GetString(Convert.FromBase64String(
|
||||||
|
base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '='))));
|
||||||
|
if (!details.Success)
|
||||||
|
continue;
|
||||||
|
tmp.method = details.Groups["method"].Value;
|
||||||
|
tmp.password = details.Groups["password"].Value;
|
||||||
|
tmp.server = details.Groups["hostname"].Value;
|
||||||
|
tmp.server_port = int.Parse(details.Groups["port"].Value);
|
||||||
|
|
||||||
|
servers.Add(tmp);
|
||||||
|
}
|
||||||
|
return servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Identifier()
|
public string Identifier()
|
||||||
|
|||||||
Reference in New Issue
Block a user