Compare commits

...
36 Commits
Author SHA1 Message Date
Syrone Wong e9ba34373b Update CHANGES and bump version
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2017-01-11 14:27:24 +08:00
Syrone Wong 4c49f1b736 Make the previous portable mode as default
- change dir name to "ss_win_temp" to make things clear.
- throw any exception caught when creating temp folder

To workaround silly cleaning softwares and careless users.

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2017-01-11 14:05:11 +08:00
破娃酱 b266bf42f5 create "help" menu group (#989) 2017-01-08 08:44:49 +08:00
Noisyfox 2e7518d802 Send first chunk with Socks5 header (#984)
* Send socks5 handshake respond before read the target address
So that the next package will (hopefully) contain the first data chunk which means we could get a 'random' first package size.

* Try read available data before send first packet

* Add random size first packet
But commented out ^u^

* Oops forget to delete some test code
2017-01-04 07:59:41 +08:00
破娃酱 18e956f34c not set alpha of the icon (#981) 2017-01-02 15:19:23 +08:00
Syrone Wong 333ad2516f Fix language detection
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 19:35:37 +08:00
Syrone Wong 9ffcf77ab1 Update LICENSE
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 16:25:41 +08:00
Syrone Wong d0d6a3cdfa Happy New Year
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 11:11:45 +08:00
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
41 changed files with 1558 additions and 1300 deletions
+18
View File
@@ -1,3 +1,21 @@
3.4.3 2017-1-11
- Make the previous portable mode as default
- Refine networking by Noisyfox
- Bug fixes and improvements
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)
+19 -19
View File
@@ -994,31 +994,31 @@ https://tls.mbed.org
License: https://raw.githubusercontent.com/ARMmbed/mbedtls/master/LICENSE
SimpleJson
Newtonsoft.Json
----------
https://github.com/facebook-csharp-sdk/simple-json
https://raw.githubusercontent.com/JamesNK/Newtonsoft.Json/master/LICENSE.md
Copyright (c) 2011, The Outercurve Foundation
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Copyright (c) 2007 James Newton-King
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ZXing
+2 -7
View File
@@ -89,11 +89,6 @@ if you want to deactivate all, please clear all textboxes.
Please visit [Servers] for more information.
#### Portable Mode
If you want to put all temporary files into shadowsocks/temp folder instead of
system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks folder.
#### Develop
[Visual Studio 2015] & [.NET Framework 4.6.2 Developer Pack] are required.
@@ -103,8 +98,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;
}
}
}
}
+13 -23
View File
@@ -7,11 +7,11 @@ namespace Shadowsocks.Controller
{
using Shadowsocks.Properties;
public class I18N
public static class I18N
{
protected static Dictionary<string, string> Strings;
private static Dictionary<string, string> _strings = new Dictionary<string, string>();
static void Init(string res)
private static void Init(string res)
{
using (var sr = new StringReader(res))
{
@@ -23,38 +23,28 @@ namespace Shadowsocks.Controller
var pos = line.IndexOf('=');
if (pos < 1)
continue;
Strings[line.Substring(0, pos)] = line.Substring(pos + 1);
_strings[line.Substring(0, pos)] = line.Substring(pos + 1);
}
}
}
static I18N()
{
Strings = new Dictionary<string, string>();
string name = CultureInfo.CurrentCulture.Name;
if (name.StartsWith("zh"))
string name = CultureInfo.CurrentCulture.EnglishName;
if (name.StartsWith("Chinese", StringComparison.OrdinalIgnoreCase))
{
if (name == "zh" || name == "zh-CN")
{
Init(Resources.cn);
}
else
{
Init(Resources.zh_tw);
}
// choose Traditional Chinese only if we get explicit indication
Init(name.Contains("Traditional")
? Resources.zh_tw
: Resources.cn);
}
}
public static string GetString(string key)
{
if (Strings.ContainsKey(key))
{
return Strings[key];
}
else
{
return key;
}
return _strings.ContainsKey(key)
? _strings[key]
: key;
}
}
}
@@ -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,164 @@
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
{
/*
* 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);
}
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();
}
}
}
}
+395 -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,256 @@ 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
{
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)
{
ReadAddress(HandleUDPAssociate);
}
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ResponseCallback(IAsyncResult ar)
{
try
{
_connection.EndSend(ar);
ReadAddress(StartConnect);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ReadAddress(Action onSuccess)
{
int atyp = _connetionRecvBuffer[3];
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1, onSuccess);
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[4];
ReadAddress(len + 2, onSuccess);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1, onSuccess);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
}
}
private void ReadAddress(int bytesRemain, Action onSuccess)
{
Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2);
// Read the remain address bytes
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, new object[] {bytesRemain, onSuccess});
}
private void OnAddressFullyRead(IAsyncResult ar)
{
if (_closed) return;
try
{
int bytesRead = _connection.EndReceive(ar);
var states = (object[]) ar.AsyncState;
int bytesRemain = (int)states[0];
var onSuccess = (Action) states[1];
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);
onSuccess.Invoke();
}
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();
}
}
// inner class
private class ProxyTimer : Timer
{
@@ -286,20 +525,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 +547,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 +556,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 +579,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 +588,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 +610,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 +631,7 @@ namespace Shadowsocks.Controller.Service
_proxyConnected = true;
if (Config.isVerboseLogging)
if (_config.isVerboseLogging)
{
if (!(remote is DirectConnect))
{
@@ -401,10 +646,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 +668,7 @@ namespace Shadowsocks.Controller.Service
timer.Enabled = false;
timer.Dispose();
if (_destConnected || Closed)
if (_destConnected || _closed)
{
return;
}
@@ -439,10 +684,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 +697,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 +710,7 @@ namespace Shadowsocks.Controller.Service
strategy?.UpdateLatency(_server, latency);
_tcprelay.UpdateLatency(_server, latency);
OnServerConnected(session);
StartPipe(session);
}
catch (ArgumentException)
{
@@ -482,14 +727,37 @@ namespace Shadowsocks.Controller.Service
}
}
protected void StartPipe(AsyncSession session)
// private static readonly Random Rnd = new Random();
private void TryReadAvailableData()
{
if (Closed) return;
int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength);
if (available > 0)
{
// Pick a random chunk size, or is it truly necessary? Random packet size is some sort of 'characteristic' itself.
//lock (Rnd)
//{
// available = Rnd.Next(available + 1);
//}
var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available,
SocketFlags.None);
_firstPacketLength += size;
}
}
private void StartPipe(AsyncSession session)
{
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);
TryReadAvailableData();
Logging.Debug($"_firstPacketLength = {_firstPacketLength}");
SendToServer(_firstPacketLength, session);
}
catch (Exception e)
{
@@ -500,10 +768,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 +781,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 +803,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 +831,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 +865,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.3";
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;
}
}
+1
View File
@@ -33,6 +33,7 @@ Check for Updates at Startup=启动时检查更新
Check Pre-release Version=检查测试版更新
Edit Hotkeys...=编辑快捷键...
About...=关于...
Help=帮助
Quit=退出
Edit Servers=编辑服务器
Load Balance=负载均衡
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.
+84 -81
View File
@@ -10,105 +10,108 @@ 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=退出
Help=幫助
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 +119,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}";
@@ -11,7 +11,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Shadowsocks")]
[assembly: AssemblyCopyright("clowwindy and community 2016")]
[assembly: AssemblyCopyright("clowwindy & community 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
+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 -31
View File
@@ -25,43 +25,26 @@ namespace Shadowsocks.Util
public static class Utils
{
private static bool? _portableMode;
private static string TempPath = null;
public static bool IsPortableMode()
{
if (!_portableMode.HasValue)
{
_portableMode = File.Exists(Path.Combine(Application.StartupPath, "shadowsocks_portable_mode.txt"));
}
return _portableMode.Value;
}
private static string _tempPath = null;
// return path to store temporary files
public static string GetTempPath()
{
if (TempPath == null)
if (_tempPath == null)
{
if (IsPortableMode())
try
{
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "temp"));
}
catch (Exception e)
{
TempPath = Path.GetTempPath();
Logging.LogUsefulException(e);
}
finally
{
// don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log
TempPath = Path.Combine(Application.StartupPath, "temp");
}
else
TempPath = Path.GetTempPath();
try
{
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "ss_win_temp"));
// don't use "/", it will fail when we call explorer /select xxx/ss_win_temp\xxx.log
_tempPath = Path.Combine(Application.StartupPath, "ss_win_temp");
}
catch (Exception e)
{
Logging.Error(e);
throw;
}
}
return TempPath;
return _tempPath;
}
// return a full path with filename combined which pointed to the temporary directory
+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 });
}
}
}
+19 -12
View File
@@ -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)
@@ -197,11 +197,16 @@ namespace Shadowsocks.View
for (int y = 0; y < iconCopy.Height; y++)
{
Color color = originIcon.GetPixel(x, y);
if (color.A != 0 && color.R > 30)
if (color.A != 0)
{
if (!enabled)
{
iconCopy.SetPixel(x, y, Color.FromArgb((byte)(color.A / 1.25), color.R, color.G, color.B));
Color flyBlue = Color.FromArgb(192, 192, 192);
// Multiply with flyBlue
int red = color.R * flyBlue.R / 255;
int green = color.G * flyBlue.G / 255;
int blue = color.B * flyBlue.B / 255;
iconCopy.SetPixel(x, y, Color.FromArgb(color.A, red, green, blue));
}
else if (global)
{
@@ -282,16 +287,18 @@ namespace Shadowsocks.View
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
CreateMenuGroup("Updates...", new MenuItem[] {
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new MenuItem("-"),
this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
this.checkPreReleaseToggleItem = CreateMenuItem("Check Pre-release Version", new EventHandler(this.checkPreReleaseToggleItem_Click)),
CreateMenuGroup("Help", new MenuItem[] {
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
CreateMenuGroup("Updates...", new MenuItem[] {
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new MenuItem("-"),
this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
this.checkPreReleaseToggleItem = CreateMenuItem("Check Pre-release Version", new EventHandler(this.checkPreReleaseToggleItem_Click)),
}),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
}),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
new MenuItem("-"),
CreateMenuItem("Quit", new EventHandler(this.Quit_Click))
});
+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">