Compare commits

...
28 Commits
Author SHA1 Message Date
Syrone Wong 8c76ac3401 Update CHANGES and bump version
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 19:24:12 +08:00
Syrone Wong 8f3994c7d8 Set LAN even if the RAS query fails
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 19:12:55 +08:00
Syrone Wong db1eafbfd8 Tweak OTA mask
sync with shadowsocks-libev

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 17:28:21 +08:00
LNDDYL 32225ed655 Update translation for Traditional Chinese (#973)
Credit to @LNDDYL
2016-12-30 17:13:46 +08:00
Syrone Wong 9e529361c4 Misc
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 21:48:30 +08:00
Syrone Wong d21162318e Workaround NotifyIcon's 63 chars limit(#965)
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 21:41:11 +08:00
Syrone Wong f9ed9174cc Fix crash when reading pac user rules
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 12:16:26 +08:00
Syrone Wong 675a5801d8 Fix appveyor link
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:39:20 +08:00
Syrone Wong 4407095d01 Fix auto startup
other minor cleanup

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 1a283aa4ea Drop obsolete tray area refreshing code
- naming private vars
- object initializer

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 97d08e4d40 Tweak privoxy config
only redirect
enable critical logging

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 673d6b3c44 update sysproxy
This will reduce false positives on virus detection.
2016-12-21 10:34:27 +08:00
noisyfox 03c087db6d Update CHANGES and bump version 2016-12-16 12:39:47 +11:00
noisyfox 5741133d5c Fix compile error.
And farewell.
2016-12-16 12:25:14 +11:00
noisyfox 28954600a2 Revert "Rename Shadowsocks.Proxy -> Shadowsocks.ForwardProxy"
This reverts commit be3cc3f3a6.
2016-12-16 12:19:52 +11:00
noisyfox 6ddd3d486b Revert "Allow LineReader to start with a first package"
This reverts commit a61b10bb67.
2016-12-16 12:19:42 +11:00
noisyfox dd269ac89d Revert "Remove privoxy"
This reverts commit 0d4d01af3b.
2016-12-16 12:19:33 +11:00
noisyfox 09d89b424d Revert "First working built-in http proxy"
This reverts commit 5ba0dffa5d.
2016-12-16 12:19:26 +11:00
noisyfox 5e973adb77 Revert "Refine header parse"
This reverts commit f9e39fdd1d.
2016-12-16 12:19:18 +11:00
noisyfox f149851e3b Revert "Better host parse"
This reverts commit d1e8f38767.
2016-12-16 12:19:09 +11:00
noisyfox 511a658fd3 Revert "Reuse buffer from first pakcage"
This reverts commit f2b8e91f81.
2016-12-16 12:18:59 +11:00
noisyfox efe6d5ba81 Revert "http -> shadowsocks protocol directly"
This reverts commit b04c59d8e6.
2016-12-16 12:18:51 +11:00
noisyfox 1f47812eca Revert "Typo and refine code"
This reverts commit 9cdb71875a.
2016-12-16 12:18:41 +11:00
noisyfox 7a39c2c71f Revert "Logging exception"
This reverts commit 566a0fcfa5.
2016-12-16 12:18:25 +11:00
noisyfox dc48546fed Revert 2016-12-16 12:17:20 +11:00
noisyfox eef990f529 Check close before create reomte
Check null ref (#940)
2016-12-15 17:23:12 +11:00
noisyfox f66638d14c Don't check and throw exception in a display function. We should check this at input side. 2016-12-15 16:51:23 +11:00
noisyfox 0c055caffd Add the missing semicolon in global proxy bypass list 2016-12-14 22:20:11 +11:00
36 changed files with 1461 additions and 1211 deletions
+13
View File
@@ -1,3 +1,16 @@
3.4.2.1 2016-12-30
- Refine Traditional Chinese translation by LNDDYL
- sysproxy: reduce false positives on virus detection
- sysproxy: set LAN proxy settings even if RAS query fails
- privoxy: drop obsolete tray area refreshing code
- Fix auto startup
- Bug fixes and improvements
3.4.2 2016-12-16
- Fix null ref in TCPRelay. (#940)
- Bring Privoxy back. (#948)
- Bug fixes and improvements.
3.4.1 (pre-release) 2016-12-13
- Fix crash if user input an invaild server address. (#933)
- Fix ERR_TOO_MANY_REDIRECTS with http proxy. (#937)
+2 -2
View File
@@ -103,8 +103,8 @@ system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks fo
GPLv3
[Appveyor]: https://ci.appveyor.com/project/Noisyfox/shadowsocks-windows
[Build Status]: https://ci.appveyor.com/api/projects/status/04idbevayrghcrh7/branch/master?svg=true
[Appveyor]: https://ci.appveyor.com/project/wongsyrone/shadowsocks-windows-pljx3
[Build Status]: https://ci.appveyor.com/api/projects/status/otie4km27ws1wak2/branch/master?svg=true
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side
+25 -3
View File
@@ -1,10 +1,11 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace Shadowsocks.Controller
{
public class FileManager
public static class FileManager
{
public static bool ByteArrayToFile(string fileName, byte[] content)
{
@@ -16,8 +17,7 @@ namespace Shadowsocks.Controller
}
catch (Exception ex)
{
Console.WriteLine("Exception caught in process: {0}",
ex.ToString());
Logging.Error(ex);
}
return false;
}
@@ -39,5 +39,27 @@ namespace Shadowsocks.Controller
}
}
}
public static string NonExclusiveReadAllText(string path)
{
return NonExclusiveReadAllText(path, Encoding.Default);
}
public static string NonExclusiveReadAllText(string path, Encoding encoding)
{
try
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var sr = new StreamReader(fs, encoding))
{
return sr.ReadToEnd();
}
}
catch (Exception ex)
{
Logging.Error(ex);
throw ex;
}
}
}
}
@@ -39,7 +39,7 @@ namespace Shadowsocks.Controller
List<string> lines = ParseResult(e.Result);
if (File.Exists(PACServer.USER_RULE_FILE))
{
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
string local = FileManager.NonExclusiveReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
using (var sr = new StringReader(local))
{
foreach (var rule in sr.NonWhiteSpaceLines())
@@ -53,7 +53,7 @@ namespace Shadowsocks.Controller
string abpContent;
if (File.Exists(PACServer.USER_ABP_FILE))
{
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
abpContent = FileManager.NonExclusiveReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
}
else
{
@@ -62,7 +62,7 @@ namespace Shadowsocks.Controller
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
if (File.Exists(PACServer.PAC_FILE))
{
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
string original = FileManager.NonExclusiveReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
if (original == abpContent)
{
UpdateCompleted(this, new ResultEventArgs(false));
@@ -1,291 +0,0 @@
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)
{
Logging.LogUsefulException(ex);
Close();
}
private static readonly Regex HttpRequestHeaderRegex = new Regex(@"^(?<method>[A-Z]+?) (?<path>[^\s]+) (?<tail>HTTP/1\.\d)$", RegexOptions.Compiled);
private int _requestLineCount = 0;
private 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 (_requestLineCount == 0)
{
var m = HttpRequestHeaderRegex.Match(line);
if (m.Success)
{
var method = m.Groups["method"].Value;
var path = m.Groups["path"].Value;
if (method == "CONNECT")
{
_isConnect = true;
if (!ParseHost(path))
{
throw new Exception("Bad http header: " + line);
}
}
else
{
var targetUrl = new Uri(path);
if (!ParseHost(targetUrl.Authority))
{
throw new Exception("Bad http header: " + line);
}
var newRequestLine = $"{method} {targetUrl.PathAndQuery} {m.Groups["tail"].Value}";
_headers.Enqueue(newRequestLine);
}
}
else
{
throw new FormatException("Not a vaild request line");
}
}
else
{
// Handle Proxy-x Headers
if (!line.StartsWith("Proxy-"))
{
_headers.Enqueue(line);
}
else
{
if (line.StartsWith("Proxy-Connection: "))
{
_headers.Enqueue(line.Substring(6));
}
}
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("Unknown 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
}
}
@@ -0,0 +1,261 @@
using System;
using System.Net;
using System.Net.Sockets;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Controller
{
class PortForwarder : Listener.Service
{
int _targetPort;
public PortForwarder(int targetPort)
{
this._targetPort = targetPort;
}
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{
if (socket.ProtocolType != ProtocolType.Tcp)
{
return false;
}
new Handler().Start(firstPacket, length, socket, this._targetPort);
return true;
}
class Handler
{
private byte[] _firstPacket;
private int _firstPacketLength;
private Socket _local;
private WrappedSocket _remote;
private bool _closed = false;
private bool _localShutdown = false;
private bool _remoteShutdown = false;
public const int RecvSize = 16384;
// remote receive buffer
private byte[] remoteRecvBuffer = new byte[RecvSize];
// connection receive buffer
private byte[] connetionRecvBuffer = new byte[RecvSize];
// instance-based lock
private readonly object _Lock = new object();
public void Start(byte[] firstPacket, int length, Socket socket, int targetPort)
{
this._firstPacket = firstPacket;
this._firstPacketLength = length;
this._local = socket;
try
{
EndPoint remoteEP = SocketUtil.GetEndPoint("127.0.0.1", targetPort);
// Connect to the remote endpoint.
_remote = new WrappedSocket();
_remote.BeginConnect(remoteEP, ConnectCallback, null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void ConnectCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_remote.EndConnect(ar);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
HandshakeReceive();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void HandshakeReceive()
{
if (_closed)
{
return;
}
try
{
_remote.BeginSend(_firstPacket, 0, _firstPacketLength, 0, new AsyncCallback(StartPipe), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void StartPipe(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_remote.EndSend(ar);
_remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
_local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeConnectionReceiveCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void PipeRemoteReceiveCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
int bytesRead = _remote.EndReceive(ar);
if (bytesRead > 0)
{
_local.BeginSend(remoteRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeConnectionSendCallback), null);
}
else
{
_local.Shutdown(SocketShutdown.Send);
_localShutdown = true;
CheckClose();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void PipeConnectionReceiveCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
int bytesRead = _local.EndReceive(ar);
if (bytesRead > 0)
{
_remote.BeginSend(connetionRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeRemoteSendCallback), null);
}
else
{
_remote.Shutdown(SocketShutdown.Send);
_remoteShutdown = true;
CheckClose();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void PipeRemoteSendCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_remote.EndSend(ar);
_local.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeConnectionReceiveCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void PipeConnectionSendCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_local.EndSend(ar);
_remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void CheckClose()
{
if (_localShutdown && _remoteShutdown)
{
this.Close();
}
}
public void Close()
{
lock (_Lock)
{
if (_closed)
{
return;
}
_closed = true;
}
if (_local != null)
{
try
{
_local.Shutdown(SocketShutdown.Both);
_local.Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
if (_remote != null)
{
try
{
_remote.Shutdown(SocketShutdown.Both);
_remote.Dispose();
}
catch (SocketException e)
{
Logging.LogUsefulException(e);
}
}
}
}
}
}
@@ -0,0 +1,172 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using Shadowsocks.Util.ProcessManagement;
namespace Shadowsocks.Controller
{
class PrivoxyRunner
{
private static int _uid;
private static string _uniqueConfigFile;
private static Job _privoxyJob;
private Process _process;
private int _runningPort;
static PrivoxyRunner()
{
try
{
_uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance.
_uniqueConfigFile = $"privoxy_{_uid}.conf";
_privoxyJob = new Job();
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe);
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll);
}
catch (IOException e)
{
Logging.LogUsefulException(e);
}
}
public int RunningPort => _runningPort;
public void Start(Configuration configuration)
{
if (_process == null)
{
Process[] existingPrivoxy = Process.GetProcessesByName("ss_privoxy");
foreach (Process p in existingPrivoxy.Where(IsChildProcess))
{
KillProcess(p);
}
string privoxyConfig = Resources.privoxy_conf;
_runningPort = GetFreePort();
privoxyConfig = privoxyConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_PORT__", _runningPort.ToString());
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(Utils.GetTempPath(_uniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig));
_process = new Process
{
// Configure the process using the StartInfo properties.
StartInfo =
{
FileName = "ss_privoxy.exe",
Arguments = _uniqueConfigFile,
WorkingDirectory = Utils.GetTempPath(),
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
CreateNoWindow = true
}
};
_process.Start();
/*
* Add this process to job obj associated with this ss process, so that
* when ss exit unexpectedly, this process will be forced killed by system.
*/
_privoxyJob.AddProcess(_process.Handle);
}
}
public void Stop()
{
if (_process != null)
{
KillProcess(_process);
_process.Dispose();
_process = null;
}
}
private static void KillProcess(Process p)
{
try
{
p.CloseMainWindow();
p.WaitForExit(100);
if (!p.HasExited)
{
p.Kill();
p.WaitForExit();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
/*
* We won't like to kill other ss instances' ss_privoxy.exe.
* This function will check whether the given process is created
* by this process by checking the module path or command line.
*
* Since it's required to put ss in different dirs to run muti instances,
* different instance will create their unique "privoxy_UID.conf" where
* UID is hash of ss's location.
*/
private static bool IsChildProcess(Process process)
{
try
{
if (Utils.IsPortableMode())
{
/*
* Under PortableMode, we could identify it by the path of ss_privoxy.exe.
*/
var path = process.MainModule.FileName;
return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
}
else
{
var cmd = process.GetCommandLine();
return cmd.Contains(_uniqueConfigFile);
}
}
catch (Exception ex)
{
/*
* Sometimes Process.GetProcessesByName will return some processes that
* are already dead, and that will cause exceptions here.
* We could simply ignore those exceptions.
*/
Logging.LogUsefulException(ex);
return false;
}
}
private int GetFreePort()
{
int defaultPort = 8123;
try
{
// TCP stack please do me a favor
TcpListener l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
var port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
catch (Exception e)
{
// in case access denied
Logging.LogUsefulException(e);
return defaultPort;
}
}
}
}
@@ -1,293 +0,0 @@
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();
}
}
}
}
+361 -153
View File
@@ -1,15 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Timers;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption;
using Shadowsocks.ForwardProxy;
using Shadowsocks.Model;
using Shadowsocks.Proxy;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Controller.Service
namespace Shadowsocks.Controller
{
class TCPRelay : Listener.Service
{
@@ -17,39 +19,23 @@ namespace Shadowsocks.Controller.Service
private DateTime _lastSweepTime;
private Configuration _config;
private readonly List<ITCPHandlerFactory> _factories = new List<ITCPHandlerFactory>();
public ISet<TCPHandler> Handlers { get; } = new HashSet<TCPHandler>();
public ISet<TCPHandler> Handlers { get; set; }
public TCPRelay(ShadowsocksController controller, Configuration conf)
{
_controller = controller;
_config = conf;
Handlers = new HashSet<TCPHandler>();
_lastSweepTime = DateTime.Now;
_factories.Add(new Socks5HandlerFactory());
_factories.Add(new HttpHandlerHandlerFactory());
}
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{
TCPHandler handler = null;
foreach (var factory in _factories)
{
if (factory.CanHandle(firstPacket, length))
{
handler = factory.NewHandler(_controller, _config, this, socket);
break;
}
}
if (handler == null)
{
if (socket.ProtocolType != ProtocolType.Tcp
|| (length < 2 || firstPacket[0] != 5))
return false;
}
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
TCPHandler handler = new TCPHandler(_controller, _config, this, socket);
IList<TCPHandler> handlersToClose = new List<TCPHandler>();
lock (Handlers)
@@ -76,7 +62,7 @@ namespace Shadowsocks.Controller.Service
* Then the handler will never release until the next Handle call. Sometimes it will
* cause odd problems (especially during memory profiling).
*/
handler.StartHandshake(firstPacket, length);
handler.Start(firstPacket, length);
return true;
}
@@ -88,7 +74,7 @@ namespace Shadowsocks.Controller.Service
{
handlersToClose.AddRange(Handlers);
}
handlersToClose.ForEach(h => h.Close());
handlersToClose.ForEach(h=>h.Close());
}
public void UpdateInboundCounter(Server server, long n)
@@ -107,40 +93,28 @@ namespace Shadowsocks.Controller.Service
}
}
interface ITCPHandlerFactory
class TCPHandler
{
bool CanHandle(byte[] firstPacket, int length);
TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket);
}
abstract class TCPHandler
{
public abstract void StartHandshake(byte[] firstPacket, int length);
protected abstract void OnServerConnected(AsyncSession session);
protected class AsyncSession
class AsyncSession
{
public IForwardProxy Remote { get; }
public IProxy Remote { get; }
public AsyncSession(IForwardProxy remote)
public AsyncSession(IProxy remote)
{
Remote = remote;
}
}
protected class AsyncSession<T> : AsyncSession
class AsyncSession<T> : AsyncSession
{
public T State { get; set; }
public AsyncSession(IForwardProxy remote, T state) : base(remote)
public AsyncSession(IProxy remote, T state) : base(remote)
{
State = state;
}
public AsyncSession(AsyncSession session, T state) : base(session.Remote)
public AsyncSession(AsyncSession session, T state): base(session.Remote)
{
State = state;
}
@@ -156,33 +130,39 @@ namespace Shadowsocks.Controller.Service
public DateTime lastActivity;
private ShadowsocksController _controller;
protected Configuration Config { get; }
private TCPRelay _tcprelay;
protected Socket Connection { get; }
private ShadowsocksController _controller;
private Configuration _config;
private TCPRelay _tcprelay;
private Socket _connection;
private IEncryptor _encryptor;
private Server _server;
private Server _server;
private AsyncSession _currentRemoteSession;
private bool _proxyConnected;
private bool _destConnected;
private bool _proxyConnected;
private bool _destConnected;
private int _totalRead = 0;
private int _totalWrite = 0;
private byte _command;
private byte[] _firstPacket;
private int _firstPacketLength;
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 int _totalRead = 0;
private int _totalWrite = 0;
private IEncryptor _encryptor;
private readonly object _encryptionLock = new object();
private readonly object _decryptionLock = new object();
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 bool _connectionShutdown = false;
private bool _remoteShutdown = false;
protected bool Closed { get; private set; }= false;
private readonly object _closeConnLock = 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 DateTime _startConnectTime;
private DateTime _startReceivingTime;
@@ -190,24 +170,21 @@ namespace Shadowsocks.Controller.Service
private EndPoint _destEndPoint = null;
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket, bool autoAppendHeader = true)
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
{
_controller = controller;
Config = config;
_config = config;
_tcprelay = tcprelay;
Connection = socket;
_connection = socket;
_proxyTimeout = config.proxy.proxyTimeout * 1000;
_serverTimeout = config.GetCurrentServer().timeout * 1000;
lastActivity = DateTime.Now;
_serverHeaderSent = !autoAppendHeader;
}
private void CreateRemote()
public 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 == "")
throw new ArgumentException("No server configured");
lock (_encryptionLock)
@@ -220,6 +197,13 @@ namespace Shadowsocks.Controller.Service
this._server = server;
}
public void Start(byte[] firstPacket, int length)
{
_firstPacket = firstPacket;
_firstPacketLength = length;
HandshakeReceive();
}
private void CheckClose()
{
if (_connectionShutdown && _remoteShutdown)
@@ -230,8 +214,8 @@ namespace Shadowsocks.Controller.Service
{
lock (_closeConnLock)
{
if (Closed) return;
Closed = true;
if (_closed) return;
_closed = true;
}
lock (_tcprelay.Handlers)
{
@@ -239,23 +223,28 @@ namespace Shadowsocks.Controller.Service
}
try
{
Connection.Shutdown(SocketShutdown.Both);
Connection.Close();
_connection.Shutdown(SocketShutdown.Both);
_connection.Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
try
if (_currentRemoteSession != null)
{
var remote = _currentRemoteSession.Remote;
remote.Shutdown(SocketShutdown.Both);
remote.Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
try
{
var remote = _currentRemoteSession.Remote;
remote.Shutdown(SocketShutdown.Both);
remote.Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
lock (_encryptionLock)
{
lock (_decryptionLock)
@@ -265,6 +254,245 @@ namespace Shadowsocks.Controller.Service
}
}
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
private class ProxyTimer : Timer
{
@@ -286,20 +514,18 @@ namespace Shadowsocks.Controller.Service
public ServerTimer(int p) : base(p) { }
}
protected void StartConnect(EndPoint target)
private void StartConnect()
{
try
{
_destEndPoint = target;
CreateRemote();
// Setting up proxy
IForwardProxy remote;
IProxy remote;
EndPoint proxyEP;
if (Config.proxy.useProxy)
if (_config.proxy.useProxy)
{
switch (Config.proxy.proxyType)
switch (_config.proxy.proxyType)
{
case ProxyConfig.PROXY_SOCKS5:
remote = new Socks5Proxy();
@@ -310,7 +536,7 @@ namespace Shadowsocks.Controller.Service
default:
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
{
@@ -319,7 +545,16 @@ namespace Shadowsocks.Controller.Service
}
var session = new AsyncSession(remote);
_currentRemoteSession = session;
lock (_closeConnLock)
{
if (_closed)
{
remote.Close();
return;
}
_currentRemoteSession = session;
}
ProxyTimer proxyTimer = new ProxyTimer(_proxyTimeout);
proxyTimer.AutoReset = false;
@@ -333,7 +568,7 @@ namespace Shadowsocks.Controller.Service
_proxyConnected = false;
// Connect to the proxy server.
remote.BeginConnectProxy(proxyEP, ProxyConnectCallback, new AsyncSession<ProxyTimer>(remote, proxyTimer));
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), new AsyncSession<ProxyTimer>(remote, proxyTimer));
}
catch (Exception e)
{
@@ -342,16 +577,15 @@ namespace Shadowsocks.Controller.Service
}
}
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{
var timer = (ProxyTimer)sender;
var timer = (ProxyTimer) sender;
timer.Elapsed -= proxyConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
if (_proxyConnected || _destConnected || Closed)
if (_proxyConnected || _destConnected || _closed)
{
return;
}
@@ -365,13 +599,13 @@ namespace Shadowsocks.Controller.Service
private void ProxyConnectCallback(IAsyncResult ar)
{
Server server = null;
if (Closed)
if (_closed)
{
return;
}
try
{
var session = (AsyncSession<ProxyTimer>)ar.AsyncState;
var session = (AsyncSession<ProxyTimer>) ar.AsyncState;
ProxyTimer timer = session.State;
var destEndPoint = timer.DestEndPoint;
server = timer.Server;
@@ -386,7 +620,7 @@ namespace Shadowsocks.Controller.Service
_proxyConnected = true;
if (Config.isVerboseLogging)
if (_config.isVerboseLogging)
{
if (!(remote is DirectConnect))
{
@@ -401,10 +635,10 @@ namespace Shadowsocks.Controller.Service
connectTimer.Enabled = true;
connectTimer.Session = session;
connectTimer.Server = server;
_destConnected = false;
// Connect to the remote endpoint.
remote.BeginConnectDest(destEndPoint, ConnectCallback, new AsyncSession<ServerTimer>(session, connectTimer));
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), new AsyncSession<ServerTimer>(session, connectTimer));
}
catch (ArgumentException)
{
@@ -423,7 +657,7 @@ namespace Shadowsocks.Controller.Service
timer.Enabled = false;
timer.Dispose();
if (_destConnected || Closed)
if (_destConnected || _closed)
{
return;
}
@@ -439,10 +673,10 @@ namespace Shadowsocks.Controller.Service
private void ConnectCallback(IAsyncResult ar)
{
if (Closed) return;
if (_closed) return;
try
{
var session = (AsyncSession<ServerTimer>)ar.AsyncState;
var session = (AsyncSession<ServerTimer>) ar.AsyncState;
ServerTimer timer = session.State;
_server = timer.Server;
timer.Elapsed -= destConnectTimer_Elapsed;
@@ -452,10 +686,10 @@ namespace Shadowsocks.Controller.Service
var remote = session.Remote;
// Complete the connection.
remote.EndConnectDest(ar);
_destConnected = true;
if (Config.isVerboseLogging)
if (_config.isVerboseLogging)
{
Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}");
}
@@ -465,7 +699,7 @@ namespace Shadowsocks.Controller.Service
strategy?.UpdateLatency(_server, latency);
_tcprelay.UpdateLatency(_server, latency);
OnServerConnected(session);
StartPipe(session);
}
catch (ArgumentException)
{
@@ -482,14 +716,14 @@ namespace Shadowsocks.Controller.Service
}
}
protected void StartPipe(AsyncSession session)
private void StartPipe(AsyncSession session)
{
if (Closed) return;
if (_closed) return;
try
{
_startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session);
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
SendToServer(_firstPacketLength, session);
}
catch (Exception e)
{
@@ -500,10 +734,10 @@ namespace Shadowsocks.Controller.Service
private void PipeRemoteReceiveCallback(IAsyncResult ar)
{
if (Closed) return;
if (_closed) return;
try
{
var session = (AsyncSession)ar.AsyncState;
var session = (AsyncSession) ar.AsyncState;
int bytesRead = session.Remote.EndReceive(ar);
_totalRead += bytesRead;
_tcprelay.UpdateInboundCounter(_server, bytesRead);
@@ -513,15 +747,15 @@ namespace Shadowsocks.Controller.Service
int bytesToSend;
lock (_decryptionLock)
{
_encryptor.Decrypt(RemoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
}
Connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, PipeConnectionSendCallback, session);
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastRead(_server);
}
else
{
Connection.Shutdown(SocketShutdown.Send);
_connection.Shutdown(SocketShutdown.Send);
_connectionShutdown = true;
CheckClose();
}
@@ -535,12 +769,12 @@ namespace Shadowsocks.Controller.Service
private void PipeConnectionReceiveCallback(IAsyncResult ar)
{
if (Closed) return;
if (_closed) return;
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;
if (bytesRead > 0)
@@ -563,53 +797,27 @@ namespace Shadowsocks.Controller.Service
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;
int bytesToSend;
lock (_encryptionLock)
{
_encryptor.Encrypt(ConnetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
_encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
}
_tcprelay.UpdateOutboundCounter(_server, bytesToSend);
_startSendingTime = DateTime.Now;
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, callback, session);
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(_server);
}
protected AsyncSession EndSendToServer(IAsyncResult ar)
{
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
return session;
}
private void PipeRemoteSendCallback(IAsyncResult ar)
{
if (Closed) return;
if (_closed) return;
try
{
var session = EndSendToServer(ar);
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session);
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
}
catch (Exception e)
{
@@ -623,8 +831,8 @@ namespace Shadowsocks.Controller.Service
try
{
var session = (AsyncSession)ar.AsyncState;
Connection.EndSend(ar);
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session);
_connection.EndSend(ar);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
}
catch (Exception e)
{
@@ -24,7 +24,7 @@ namespace Shadowsocks.Controller
public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted;
public const string Version = "3.4.1";
public const string Version = "3.4.2.1";
private class CheckUpdateTimer : System.Timers.Timer
{
@@ -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;
@@ -30,6 +30,7 @@ namespace Shadowsocks.Controller
private PACServer _pacServer;
private Configuration _config;
private StrategyManager _strategyManager;
private PrivoxyRunner privoxyRunner;
private GFWListUpdater gfwListUpdater;
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance;
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }
@@ -264,6 +265,10 @@ namespace Shadowsocks.Controller
{
_listener.Stop();
}
if (privoxyRunner != null)
{
privoxyRunner.Stop();
}
if (_config.enabled)
{
SystemProxy.Update(_config, true, null);
@@ -427,7 +432,11 @@ namespace Shadowsocks.Controller
// some logic in configuration updated the config when saving, we need to read it again
_config = Configuration.Load();
StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
if (privoxyRunner == null)
{
privoxyRunner = new PrivoxyRunner();
}
if (_pacServer == null)
{
_pacServer = new PACServer();
@@ -448,7 +457,11 @@ namespace Shadowsocks.Controller
{
_listener.Stop();
}
// don't put PrivoxyRunner.Start() before pacServer.Stop()
// or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1
// though UseShellExecute is set to true now
// http://stackoverflow.com/questions/10235093/socket-doesnt-close-after-application-exits-if-a-launched-process-is-open
privoxyRunner.Stop();
try
{
var strategy = GetCurrentStrategy();
@@ -457,12 +470,15 @@ namespace Shadowsocks.Controller
strategy.ReloadServers();
}
privoxyRunner.Start(_config);
TCPRelay tcpRelay = new TCPRelay(this, _config);
UDPRelay udpRelay = new UDPRelay(this);
List<Listener.IService> services = new List<Listener.IService>();
services.Add(_pacServer);
services.Add(tcpRelay);
services.Add(udpRelay);
services.Add(_pacServer);
services.Add(new PortForwarder(privoxyRunner.RunningPort));
_listener = new Listener(services);
_listener.Start(_config);
}
@@ -541,10 +557,10 @@ namespace Shadowsocks.Controller
UpdatePACFromGFWList();
return;
}
List<string> lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath("gfwlist.txt")));
List<string> lines = GFWListUpdater.ParseResult(FileManager.NonExclusiveReadAllText(Utils.GetTempPath("gfwlist.txt")));
if (File.Exists(PACServer.USER_RULE_FILE))
{
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
string local = FileManager.NonExclusiveReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
using (var sr = new StringReader(local))
{
foreach (var rule in sr.NonWhiteSpaceLines())
@@ -558,7 +574,7 @@ namespace Shadowsocks.Controller
string abpContent;
if (File.Exists(PACServer.USER_ABP_FILE))
{
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
abpContent = FileManager.NonExclusiveReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
}
else
{
@@ -567,7 +583,7 @@ namespace Shadowsocks.Controller
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
if (File.Exists(PACServer.PAC_FILE))
{
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
string original = FileManager.NonExclusiveReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
if (original == abpContent)
{
return;
@@ -1,20 +1,24 @@
using System;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.Win32;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
class AutoStartup
static class AutoStartup
{
static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode();
// Don't use Application.ExecutablePath
// see https://stackoverflow.com/questions/12945805/odd-c-sharp-path-issue
private static readonly string ExecutablePath = Assembly.GetEntryAssembly().Location;
private static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode();
public static bool Set(bool enabled)
{
RegistryKey runKey = null;
try
{
string path = Application.ExecutablePath;
runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
if ( runKey == null ) {
Logging.Error( @"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run" );
@@ -22,7 +26,7 @@ namespace Shadowsocks.Controller
}
if (enabled)
{
runKey.SetValue(Key, path);
runKey.SetValue(Key, ExecutablePath);
}
else
{
@@ -53,7 +57,6 @@ namespace Shadowsocks.Controller
RegistryKey runKey = null;
try
{
string path = Application.ExecutablePath;
runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
if (runKey == null) {
Logging.Error(@"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run");
@@ -67,10 +70,10 @@ namespace Shadowsocks.Controller
else if (item.Equals("Shadowsocks", StringComparison.OrdinalIgnoreCase)) // Compatibility with older versions
{
string value = Convert.ToString(runKey.GetValue(item));
if (path.Equals(value, StringComparison.OrdinalIgnoreCase))
if (ExecutablePath.Equals(value, StringComparison.OrdinalIgnoreCase))
{
runKey.DeleteValue(item);
runKey.SetValue(Key, path);
runKey.SetValue(Key, ExecutablePath);
return true;
}
}
Binary file not shown.
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__
toggle 0
logfile ss_privoxy.log
show-on-task-bar 0
activity-animation 0
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
hide-console
Binary file not shown.
Binary file not shown.
+83 -81
View File
@@ -10,105 +10,107 @@ PAC=PAC 模式
Global=全局模式
Servers=伺服器
Edit Servers...=編輯伺服器...
Statistics Config...=統計配置...
Statistics Config...=統計設定檔...
Start on Boot=開機啟動
Forward Proxy...=正向代理設置...
Allow Clients from LAN=允許來自區域網路的連接
Local PAC=使用本地 PAC
Online PAC=使用在線 PAC
Edit Local PAC File...=編輯本地 PAC 文件...
Update Local PAC from GFWList=從 GFWList 更新本地 PAC
Edit User Rule for GFWList...=編輯 GFWList 的用戶規則...
Secure Local PAC=保護本地 PAC
Copy Local PAC URL=複製本地 PAC 網址
Share Server Config...=分享伺服器配置...
Forward Proxy...=正向 Proxy 設定...
Allow Clients from LAN=允許來自區域網路的連線
Local PAC=使用本機 PAC
Online PAC=使用線上 PAC
Edit Local PAC File...=編輯本機 PAC 檔案...
Update Local PAC from GFWList=從 GFWList 更新本機 PAC
Edit User Rule for GFWList...=編輯 GFWList 的使用者規則...
Secure Local PAC=安全本機 PAC
Copy Local PAC URL=複製本機 PAC 網址
Share Server Config...=分享伺服器設定檔...
Scan QRCode from Screen...=掃描螢幕上的 QR 碼...
Import URL from Clipboard...=從剪貼板導入URL...
Import URL from Clipboard...=從剪貼簿匯入 URL...
Availability Statistics=統計可用性
Show Logs...=顯示日誌...
Verbose Logging=詳細記錄日誌
Show Logs...=顯示記錄檔...
Verbose Logging=詳細資訊記錄
Updates...=更新...
Check for Updates...=檢查更新
Check for Updates at Startup=啟動時檢查更新
Check Pre-release Version=檢查測試版更新
Edit Hotkeys...=編輯捷徑鍵...
Check Pre-release Version=檢查發行前版本更新
Edit Hotkeys...=編輯快速鍵...
About...=關於...
Quit=退出
Quit=結束
Edit Servers=編輯伺服器
Load Balance=負載均衡
High Availability=高可用
Load Balance=負載平衡
High Availability=高可用性
Choose by statistics=根據統計
# Config Form
&Add=新增(&A)
&Delete=移除(&D)
Dupli&cate=複製(&C)
&Add=新增 (&A)
&Delete=移除 (&D)
Dupli&cate=複製 (&C)
Server=伺服器
Server Addr=伺服器位址
Server Port=伺服器連接埠
Password=密碼
Encryption=加密
Proxy Port=代理連接埠
Remarks=備註
Timeout(Sec)=超時(秒)
Proxy Port=Proxy 連接埠
Remarks=註解
Timeout(Sec)=逾時 (秒)
Onetime Authentication=單次驗證
OK=確定
Cancel=取消
New server=未配置的伺服器
Move &Up=上移(&U)
Move D&own=下移(&O)
New server=新伺服器
Move &Up=上移 (&U)
Move D&own=下移 (&O)
# Proxy Form
Edit Proxy=代理設置
Use Proxy=使用代理
Proxy Type=代理類別
Proxy Addr=代理位址
Proxy Port=代理連接埠
Edit Proxy=編輯 Proxy
Use Proxy=使用 Proxy
Proxy Type=Proxy 類型
Proxy Addr=Proxy 位址
Proxy Port=Proxy 連接埠
# Log Form
&File=文件(&F)
&Open Location=在資料總管中打開(&O)
E&xit=退出(&X)
&View=視圖(&V)
&Clean Logs=清空日誌(&C)
Change &Font=設置字體(&F)
&Wrap Text=自動換行(&W)
&Top Most=置頂(&T)
&Show Toolbar=顯示工具欄(&S)
Log Viewer=日誌查看器
Inbound=入站
Outbound=出站
&File=檔案 (&F)
&Open Location=在檔案總管中開啟 (&O)
E&xit=結束 (&X)
&View=檢視
(&V)
&Clean Logs=清除記錄檔 (&C)
Change &Font=變更字型 (&F)
&Wrap Text=自動換行 (&W)
&Top Most=置頂 (&T)
&Show Toolbar=顯示工具列 (&S)
Log Viewer=記錄檔檢視器
Inbound=輸入
Outbound=輸出
# QRCode Form
QRCode and URL=QR 碼与 URL
QRCode and URL=QR 碼與 URL
# PAC Url Form
Edit Online PAC URL=編輯在線 PAC 網址
Edit Online PAC URL...=編輯在線 PAC 網址...
Edit Online PAC URL=編輯線上 PAC 網址
Edit Online PAC URL...=編輯線上 PAC 網址...
Please input PAC Url=請輸入 PAC 網址
# HotkeySettings Form
Switch system proxy=切換系統代理狀態
Switch system proxy mode=切換系統代理模式
Switch share over LAN=切換區域網路共享
Show Logs=顯示日誌
Switch to prev server=切換上個伺服器
Switch to next server=切換下個伺服器
Reg All=註冊全部捷徑鍵
Switch system proxy=切換系統 Proxy 狀態
Switch system proxy mode=切換系統 Proxy 模式
Switch share over LAN=切換區域網路共用
Show Logs=顯示記錄檔
Switch to prev server=切換上一個伺服器
Switch to next server=切換下一個伺服器
Reg All=註冊所有快速鍵
# Messages
Shadowsocks Error: {0}=Shadowsocks 錯誤: {0}
Port already in use=連接埠號碼已被占用
Invalid server address=非法伺服器位址
Illegal port number format=非法連接埠號碼格式
Illegal timeout format=非法超時格式
Port already in use=連接埠號碼已被使用
Invalid server address=無效伺服器位址
Illegal port number format=無效連接埠號碼格式
Illegal timeout format=無效逾時格式
Please add at least one server=請新增至少一個伺服器
Server IP can not be blank=伺服器 IP 不能為空
Password can not be blank=密碼不能為空
@@ -116,27 +118,27 @@ Port out of range=連接埠號碼超出範圍
Port can't be 8123=連接埠號碼不能為 8123
Shadowsocks {0} Update Found=Shadowsocks {0} 更新
No update is available=沒有可用的更新
Click here to update=點擊這裏升級
Shadowsocks is here=Shadowsocks 在這裏
You can turn on/off Shadowsocks in the context menu=可以在右鍵菜單中開關 Shadowsocks
System Proxy Enabled=系統代理已啟用
System Proxy Disabled=系統代理未啟用
Failed to update PAC file =更新 PAC 文件失敗
Click here to update=點按此處升級
Shadowsocks is here=Shadowsocks 在這裡
You can turn on/off Shadowsocks in the context menu=可以在右鍵選項單中開關 Shadowsocks
System Proxy Enabled=系統 Proxy 已啟用
System Proxy Disabled=系統 Proxy 未啟用
Failed to update PAC file =更新 PAC 檔案失敗
PAC updated=更新 PAC 成功
No updates found. Please report to GFWList if you have problems with it.=未發現更新。如有問題請提交給 GFWList。
No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近屏幕中間的位置
Shadowsocks is already running.=Shadowsocks 已經在運行。
Find Shadowsocks icon in your notify tray.=請在工作列裏尋找 Shadowsocks 圖標。
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制一份到別的目錄。
Failed to decode QRCode=無法解析 QR 碼
No updates found. Please report to GFWList if you have problems with it.=未發現更新。如有問題請報告至 GFWList。
No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近熒幕中間的位置
Shadowsocks is already running.=Shadowsocks 已經在執行。
Find Shadowsocks icon in your notify tray.=請在工作列裡尋找 Shadowsocks 圖示。
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外複製一份至別的目錄。
Failed to decode QRCode=QR 碼解碼失敗
Failed to update registry=無法修改登錄檔
System Proxy On: =系統代理已啟用:
Running: Port {0}=正在運行:連接埠號碼 {0}
Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shadowsocks將退出。請提交此錯誤到
Unsupported operating system, use Windows Vista at least.=不支持的作業系統版本,最低需求為Windows Vista。
Unsupported .NET Framework, please update to 4.6.2 or later.=當前 .NET Framework 版本過低,最低需求為4.6.2。
Proxy request failed=代理請求失敗
Proxy handshake failed=代理握手失敗
Register hotkey failed=註冊捷徑鍵失敗
Cannot parse hotkey: {0}=解析捷徑鍵失敗: {0}
Timeout is invalid, it should not exceed {0}=超時無效,不應超過 {0}
System Proxy On: =系統 Proxy 已啟用:
Running: Port {0}=正在執行:連接埠號碼 {0}
Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shadowsocks 將結束。請報告此錯誤至
Unsupported operating system, use Windows Vista at least.=不支援的作業系統版本,最低需求為 Windows Vista。
Unsupported .NET Framework, please update to 4.6.2 or later.=目前 .NET Framework 版本過低,最低需求為4.6.2。
Proxy request failed=Proxy 要求失敗
Proxy handshake failed=Proxy 交握失敗
Register hotkey failed=註冊快速鍵失敗
Cannot parse hotkey: {0}=剖析快速鍵失敗: {0}
Timeout is invalid, it should not exceed {0}=逾時無效,不應超過 {0}
+1 -1
View File
@@ -106,7 +106,7 @@ namespace Shadowsocks.Encryption
#region OneTimeAuth
public const int ONETIMEAUTH_FLAG = 0x10;
public const int ADDRTYPE_MASK = 0xF;
public const int ADDRTYPE_MASK = 0xEF;
public const int ONETIMEAUTH_BYTES = 10;
+1 -3
View File
@@ -45,9 +45,7 @@ namespace Shadowsocks.Model
string serverStr;
// CheckHostName() won't do a real DNS lookup
var hostType = Uri.CheckHostName( server );
if ( hostType == UriHostNameType.Unknown ) {
throw new FormatException("Invalid Server Address.");
}
switch ( hostType ) {
case UriHostNameType.IPv6:
serverStr = $"[{server}]:{server_port}";
+57 -23
View File
@@ -111,6 +111,40 @@ namespace Shadowsocks.Properties {
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] mgwz_dll {
get {
object obj = ResourceManager.GetObject("mgwz_dll", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// 查找类似 listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__
///show-on-task-bar 0
///activity-animation 0
///forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
///hide-console
/// 的本地化字符串。
/// </summary>
internal static string privoxy_conf {
get {
return ResourceManager.GetString("privoxy_conf", resourceCulture);
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] privoxy_exe {
get {
object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
@@ -213,29 +247,29 @@ namespace Shadowsocks.Properties {
}
/// <summary>
/// 查找类似 # translation for Traditional Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=啟用系統代理
///Mode=系統代理模式
///PAC=PAC 模式
///Global=全局模式
///Servers=伺服器
///Edit Servers...=編輯伺服器...
///Statistics Config...=統計配置...
///Start on Boot=開機啟動
///Forward Proxy...=正向代理設置...
///Allow Clients from LAN=允許來自區域網路的連接
///Local PAC=使用本地 PAC
///Online PAC=使用在線 PAC
///Edit Local PAC File...=編輯本地 PAC 文件...
///Update Local PAC from GFWList=從 GFWList 更新本地 PAC
///Edit User Rule for GFWList...=編輯 GFWList 的用戶規則...
///Show QRCode...=顯示 QR 碼...
///S [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// 查找类似 # translation for Traditional Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=啟用系統代理
///Mode=系統代理模式
///PAC=PAC 模式
///Global=全局模式
///Servers=伺服器
///Edit Servers...=編輯伺服器...
///Statistics Config...=統計配置...
///Start on Boot=開機啟動
///Forward Proxy...=正向代理設置...
///Allow Clients from LAN=允許來自區域網路的連接
///Local PAC=使用本地 PAC
///Online PAC=使用在線 PAC
///Edit Local PAC File...=編輯本地 PAC 文件...
///Update Local PAC from GFWList=從 GFWList 更新本地 PAC
///Edit User Rule for GFWList...=編輯 GFWList 的用戶規則...
///Show QRCode...=顯示 QR 碼...
///Scan QRCode from Screen [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// </summary>
internal static string zh_tw {
get {
@@ -127,6 +127,15 @@
<data name="libsscrypto_dll" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\libsscrypto.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="mgwz_dll" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\mgwz.dll.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="privoxy_conf" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\privoxy_conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="privoxy_exe" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\privoxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="proxy_pac_txt" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Data\proxy.pac.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
@@ -4,9 +4,9 @@ using System.Net.Sockets;
using System.Threading;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.ForwardProxy
namespace Shadowsocks.Proxy
{
public class DirectConnect : IForwardProxy
public class DirectConnect : IProxy
{
private class FakeAsyncResult : IAsyncResult
{
@@ -7,9 +7,9 @@ using System.Threading;
using Shadowsocks.Controller;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.ForwardProxy
namespace Shadowsocks.Proxy
{
public class HttpProxy : IForwardProxy
public class HttpProxy : IProxy
{
private class FakeAsyncResult : IAsyncResult
{
@@ -134,7 +134,7 @@ namespace Shadowsocks.ForwardProxy
_remote.EndSend(ar);
// start line read
new LineReader(1024, _remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, new FakeAsyncResult(ar, state));
new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state));
}
catch (Exception ex)
{
@@ -2,10 +2,10 @@
using System.Net;
using System.Net.Sockets;
namespace Shadowsocks.ForwardProxy
namespace Shadowsocks.Proxy
{
public interface IForwardProxy
public interface IProxy
{
EndPoint LocalEndPoint { get; }
@@ -6,9 +6,9 @@ using System.Threading;
using Shadowsocks.Controller;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.ForwardProxy
namespace Shadowsocks.Proxy
{
public class Socks5Proxy : IForwardProxy
public class Socks5Proxy : IProxy
{
private class FakeAsyncResult : IAsyncResult
{
@@ -73,15 +73,52 @@ namespace Shadowsocks.ForwardProxy
{
DestEndPoint = destEndPoint;
int len = Socks5Util.HeaderAddrLength(destEndPoint);
byte[] request = null;
byte atyp = 0;
int port;
byte[] request = new byte[len + 3];
Socks5Util.FillHeaderAddr(request, 3, 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);
}
// 构造request包剩余部分
request[0] = 5;
request[1] = 1;
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();
st.Callback = callback;
@@ -0,0 +1,179 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Shadowsocks.Controller;
namespace Shadowsocks.Util.ProcessManagement
{
/*
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
public class Job : IDisposable
{
private IntPtr handle = IntPtr.Zero;
public Job()
{
handle = CreateJobObject(IntPtr.Zero, null);
var extendedInfoPtr = IntPtr.Zero;
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{
LimitFlags = 0x2000
};
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation = info
};
try
{
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint) length))
throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error()));
}
finally
{
if (extendedInfoPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(extendedInfoPtr);
extendedInfoPtr = IntPtr.Zero;
}
}
}
public bool AddProcess(IntPtr processHandle)
{
var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{
Logging.Error("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
}
return succ;
}
public bool AddProcess(int processId)
{
return AddProcess(Process.GetProcessById(processId).Handle);
}
#region IDisposable
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
if (disposing)
{
// no managed objects to free
}
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
}
~Job()
{
Dispose(false);
}
#endregion
#region Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr CreateJobObject(IntPtr a, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion
}
#region Helper classes
[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public UInt32 LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit;
public UIntPtr Affinity;
public UInt32 PriorityClass;
public UInt32 SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UIntPtr ProcessMemoryLimit;
public UIntPtr JobMemoryLimit;
public UIntPtr PeakProcessMemoryUsed;
public UIntPtr PeakJobMemoryUsed;
}
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
#endregion
}
@@ -0,0 +1,31 @@
using System.Diagnostics;
using System.Management;
using System.Text;
namespace Shadowsocks.Util.ProcessManagement
{
static class ThreadUtil
{
/*
* See:
* http://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c
*/
public static string GetCommandLine(this Process process)
{
var commandLine = new StringBuilder(process.MainModule.FileName);
commandLine.Append(" ");
using (var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id))
{
foreach (var @object in searcher.Get())
{
commandLine.Append(@object["CommandLine"]);
commandLine.Append(" ");
}
}
return commandLine.ToString();
}
}
}
@@ -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;
}
}
}
+143 -108
View File
@@ -12,24 +12,17 @@ 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;
private int _bufferDataIndex;
private int _bufferDataLength;
private int _bufferIndex;
public LineReader(byte[] buffer, WrappedSocket socket, byte[] firstPackge, int index, int length,
Func<string, object, bool> onLineRead, Action<Exception, object> onException,
Action<byte[], int, int, object> onFinish,
Encoding encoding, string delimiter,
object state)
public LineReader(WrappedSocket socket, Func<string, object, bool> onLineRead, Action<Exception, object> onException,
Action<byte[], int, int, object> onFinish, Encoding encoding, string delimiter, int maxLineBytes, object state)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (socket == null)
{
throw new ArgumentNullException(nameof(socket));
@@ -47,19 +40,6 @@ namespace Shadowsocks.Util.Sockets
throw new ArgumentNullException(nameof(delimiter));
}
if (buffer.Length < length)
{
throw new ArgumentException("Line buffer length can't less than first package length!", nameof(buffer));
}
if (length > 0)
{
if (firstPackge == null)
{
throw new ArgumentNullException(nameof(firstPackge));
}
}
_socket = socket;
_onLineRead = onLineRead;
_onException = onException;
@@ -76,121 +56,176 @@ namespace Shadowsocks.Util.Sockets
throw new ArgumentException("Too short!", nameof(delimiter));
}
if (buffer.Length < _delimiterBytes.Length)
if (maxLineBytes < _delimiterBytes.Length)
{
throw new ArgumentException("Too small!", nameof(buffer));
throw new ArgumentException("Too small!", nameof(maxLineBytes));
}
_delimiterSearch = new ByteSearch.SearchTarget(_delimiterBytes);
_delimiterSearchCharTable = MakeCharTable(_delimiterBytes);
_delimiterSearchOffsetTable = MakeOffsetTable(_delimiterBytes);
_lineBuffer = buffer;
_lineBuffer = new byte[maxLineBytes];
if (length > 0)
{
// process first package
if (buffer == firstPackge)
{
_bufferDataIndex = index;
}
else
{
Array.Copy(firstPackge, index, _lineBuffer, 0, length);
}
_bufferDataLength = length;
try
{
NewPackageRecv();
}
catch (Exception ex)
{
OnException(ex);
OnFinish();
}
}
else
{
// start reading
socket.BeginReceive(_lineBuffer, 0, _lineBuffer.Length, 0, ReceiveCallback, 0);
}
}
public LineReader(int maxLineBytes, WrappedSocket socket, Func<string, object, bool> onLineRead,
Action<Exception, object> onException, Action<byte[], int, int, object> onFinish, Encoding encoding,
string delimiter, object state)
: this(
new byte[maxLineBytes], socket, null, 0, 0, onLineRead, onException, onFinish, encoding, delimiter,
state)
{
// start reading
socket.BeginReceive(_lineBuffer, 0, maxLineBytes, 0, ReceiveCallback, 0);
}
private void ReceiveCallback(IAsyncResult ar)
{
int length = (int)ar.AsyncState;
try
{
var bytesRead = _socket.EndReceive(ar);
if (bytesRead == 0)
{
OnFinish();
OnFinish(length);
return;
}
_bufferDataLength += bytesRead;
length += bytesRead;
NewPackageRecv();
int i;
while ((i = IndexOf(_lineBuffer, _bufferIndex, length, _delimiterBytes, _delimiterSearchOffsetTable,
_delimiterSearchCharTable)) != -1)
{
var decodeLen = i - _bufferIndex;
string line = _encoding.GetString(_lineBuffer, _bufferIndex, decodeLen);
_bufferIndex = i + _delimiterBytes.Length;
length -= decodeLen;
length -= _delimiterBytes.Length;
var stop = _onLineRead(line, _state);
if (stop)
{
OnFinish(length);
return;
}
}
if (length == _lineBuffer.Length)
{
OnException(new IndexOutOfRangeException("LineBuffer full! Try increace maxLineBytes!"));
OnFinish(length);
return;
}
if (_bufferIndex > 0)
{
Buffer.BlockCopy(_lineBuffer, _bufferIndex, _lineBuffer, 0, length);
_bufferIndex = 0;
}
_socket.BeginReceive(_lineBuffer, length, _lineBuffer.Length - length, 0, ReceiveCallback, length);
}
catch (Exception ex)
{
OnException(ex);
OnFinish();
OnFinish(length);
}
}
private void NewPackageRecv()
{
int i;
while ((i = _delimiterSearch.SearchIn(_lineBuffer, _bufferDataIndex, _bufferDataLength)) != -1)
{
var decodeLen = i - _bufferDataIndex;
string line = _encoding.GetString(_lineBuffer, _bufferDataIndex, decodeLen);
_bufferDataIndex = i + _delimiterBytes.Length;
_bufferDataLength -= decodeLen;
_bufferDataLength -= _delimiterBytes.Length;
var stop = _onLineRead(line, _state);
if (stop)
{
OnFinish();
return;
}
}
if (_bufferDataLength == _lineBuffer.Length)
{
OnException(new IndexOutOfRangeException("LineBuffer full! Try increace maxLineBytes!"));
OnFinish();
return;
}
if (_bufferDataIndex > 0)
{
Buffer.BlockCopy(_lineBuffer, _bufferDataIndex, _lineBuffer, 0, _bufferDataLength);
_bufferDataIndex = 0;
}
_socket.BeginReceive(_lineBuffer, _bufferDataLength, _lineBuffer.Length - _bufferDataLength, 0, ReceiveCallback, _bufferDataLength);
}
private void OnException(Exception ex)
{
_onException?.Invoke(ex, _state);
}
private void OnFinish()
private void OnFinish(int length)
{
_onFinish?.Invoke(_lineBuffer, _bufferDataIndex, _bufferDataLength, _state);
_onFinish?.Invoke(_lineBuffer, _bufferIndex, length, _state);
}
#region Boyer-Moore string search
public 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
}
}
@@ -1,85 +0,0 @@
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;
}
}
}
@@ -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)
@@ -44,7 +44,7 @@ namespace Shadowsocks.Util.SystemProxy
{
if (global)
{
arguments = $"global {proxyServer} <local>localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;172.32.*;192.168.*";
arguments = $"global {proxyServer} <local>;localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;172.32.*;192.168.*";
}
else
{
+14
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
namespace Shadowsocks.Util
@@ -17,5 +18,18 @@ namespace Shadowsocks.Util
var children = control.Controls.OfType<TControl>().ToList();
return children.SelectMany(GetChildControls<TControl>).Concat(children);
}
// Workaround NotifyIcon's 63 chars limit
// https://stackoverflow.com/questions/579665/how-can-i-show-a-systray-tooltip-longer-than-63-chars
public static void SetNotifyIconText(NotifyIcon ni, string text)
{
if (text.Length >= 128)
throw new ArgumentOutOfRangeException("Text limited to 127 characters");
Type t = typeof(NotifyIcon);
BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance;
t.GetField("text", hidden).SetValue(ni, text);
if ((bool)t.GetField("added", hidden).GetValue(ni))
t.GetMethod("UpdateIcon", hidden).Invoke(ni, new object[] { true });
}
}
}
@@ -180,13 +180,13 @@ namespace Shadowsocks.View
{
serverInfo = config.GetCurrentServer().FriendlyName();
}
// we want to show more details but notify icon title is limited to 63 characters
// show more info by hacking the P/Invoke declaration for NOTIFYICONDATA inside Windows Forms
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
(enabled ?
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ "\n" + serverInfo;
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length));
ViewUtils.SetNotifyIconText(_notifyIcon, text);
}
private Bitmap getTrayIconByState(Bitmap originIcon, bool enabled, bool global)
+12 -9
View File
@@ -140,9 +140,6 @@
<Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.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="Model\HotKeyConfig.cs" />
<Compile Include="Model\ProxyConfig.cs" />
@@ -151,9 +148,9 @@
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Include="ForwardProxy\DirectConnect.cs" />
<Compile Include="ForwardProxy\HttpProxy.cs" />
<Compile Include="ForwardProxy\IForwardProxy.cs" />
<Compile Include="Proxy\DirectConnect.cs" />
<Compile Include="Proxy\HttpProxy.cs" />
<Compile Include="Proxy\IProxy.cs" />
<Compile Include="Controller\Service\AvailabilityStatistics.cs" />
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" />
<Compile Include="Controller\Strategy\StatisticsStrategy.cs" />
@@ -163,6 +160,7 @@
<Compile Include="Controller\I18N.cs" />
<Compile Include="Controller\Service\Listener.cs" />
<Compile Include="Controller\Logging.cs" />
<Compile Include="Controller\Service\PortForwarder.cs" />
<Compile Include="Controller\Service\UDPRelay.cs" />
<Compile Include="Controller\Service\UpdateChecker.cs" />
<Compile Include="Encryption\EncryptorBase.cs" />
@@ -187,15 +185,15 @@
<Compile Include="Controller\Strategy\BalancingStrategy.cs" />
<Compile Include="Controller\Strategy\StrategyManager.cs" />
<Compile Include="Controller\Strategy\IStrategy.cs" />
<Compile Include="ForwardProxy\Socks5Proxy.cs" />
<Compile Include="Proxy\Socks5Proxy.cs" />
<Compile Include="Settings.cs" />
<Compile Include="StringEx.cs" />
<Compile Include="Controller\System\Hotkeys\Hotkeys.cs" />
<Compile Include="Util\ProcessManagement\Job.cs" />
<Compile Include="Util\ProcessManagement\ThreadUtil.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\Socks5Util.cs" />
<Compile Include="Util\Sockets\WrappedSocket.cs" />
<Compile Include="Util\SystemProxy\ProxyException.cs" />
<Compile Include="Util\SystemProxy\Sysproxy.cs" />
@@ -207,6 +205,8 @@
<Compile Include="View\ConfigForm.Designer.cs">
<DependentUpon>ConfigForm.cs</DependentUpon>
</Compile>
<Compile Include="Controller\Service\TCPRelay.cs" />
<Compile Include="Controller\Service\PrivoxyRunner.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Controller\ShadowsocksController.cs" />
@@ -284,6 +284,8 @@
</None>
<None Include="Data\abp.js.gz" />
<None Include="Data\libsscrypto.dll.gz" />
<None Include="Data\mgwz.dll.gz" />
<None Include="Data\privoxy.exe.gz" />
<None Include="Data\proxy.pac.txt.gz" />
</ItemGroup>
<ItemGroup>
@@ -300,6 +302,7 @@
<None Include="Resources\ss24.png" />
<None Include="Resources\ssw128.png" />
<Content Include="Data\cn.txt" />
<Content Include="Data\privoxy_conf.txt" />
<Content Include="Data\user-rule.txt" />
<Content Include="Data\zh_tw.txt" />
<Content Include="FodyWeavers.xml">