Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f910400449 | ||
|
|
3ba5c491d2 | ||
|
|
13ebb5ef01 | ||
|
|
c46149a375 | ||
|
|
6e8d6d3d21 | ||
|
|
c7adac5fb0 | ||
|
|
41eb43b54e | ||
|
|
46d95e7411 | ||
|
|
d5f24df128 | ||
|
|
a0fd003099 | ||
|
|
4bd9fd8a6b | ||
|
|
1dc992e806 | ||
|
|
4acae9de9b | ||
|
|
35fc73b58b | ||
|
|
156b3acd9b | ||
|
|
8cf582803d | ||
|
|
c8d070fb09 | ||
|
|
c671c7c32d |
@@ -1,3 +1,7 @@
|
||||
3.3.2 2016-10-03
|
||||
- Add HTTP forward proxy support
|
||||
- Bug fixes and improvements
|
||||
|
||||
3.3.1 2016-09-20
|
||||
- Add global hotkey support
|
||||
- Bug fixes and improvements
|
||||
|
||||
@@ -39,6 +39,9 @@ namespace Shadowsocks.Controller
|
||||
// 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;
|
||||
@@ -220,7 +223,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
public void Close()
|
||||
{
|
||||
lock (this)
|
||||
lock (_Lock)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
|
||||
+13
-13
@@ -16,21 +16,21 @@ using Shadowsocks.Util.ProcessManagement;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
class PolipoRunner
|
||||
class PrivoxyRunner
|
||||
{
|
||||
private static int Uid;
|
||||
private static string UniqueConfigFile;
|
||||
private static Job PolipoJob;
|
||||
private static Job PrivoxyJob;
|
||||
private Process _process;
|
||||
private int _runningPort;
|
||||
|
||||
static PolipoRunner()
|
||||
static PrivoxyRunner()
|
||||
{
|
||||
try
|
||||
{
|
||||
Uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different polipo instance.
|
||||
Uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance.
|
||||
UniqueConfigFile = $"privoxy_{Uid}.conf";
|
||||
PolipoJob = new Job();
|
||||
PrivoxyJob = new Job();
|
||||
|
||||
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe);
|
||||
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll);
|
||||
@@ -54,17 +54,17 @@ namespace Shadowsocks.Controller
|
||||
Server server = configuration.GetCurrentServer();
|
||||
if (_process == null)
|
||||
{
|
||||
Process[] existingPolipo = Process.GetProcessesByName("ss_privoxy");
|
||||
foreach (Process p in existingPolipo.Where(IsChildProcess))
|
||||
Process[] existingPrivoxy = Process.GetProcessesByName("ss_privoxy");
|
||||
foreach (Process p in existingPrivoxy.Where(IsChildProcess))
|
||||
{
|
||||
KillProcess(p);
|
||||
}
|
||||
string polipoConfig = Resources.privoxy_conf;
|
||||
string privoxyConfig = Resources.privoxy_conf;
|
||||
_runningPort = this.GetFreePort();
|
||||
polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
|
||||
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.ToString());
|
||||
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
|
||||
FileManager.ByteArrayToFile(Utils.GetTempPath(UniqueConfigFile), Encoding.UTF8.GetBytes(polipoConfig));
|
||||
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.
|
||||
@@ -80,7 +80,7 @@ namespace Shadowsocks.Controller
|
||||
* 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.
|
||||
*/
|
||||
PolipoJob.AddProcess(_process.Handle);
|
||||
PrivoxyJob.AddProcess(_process.Handle);
|
||||
}
|
||||
RefreshTrayArea();
|
||||
}
|
||||
@@ -139,8 +139,6 @@ namespace Shadowsocks.Controller
|
||||
|
||||
private AsyncSession _currentRemoteSession;
|
||||
|
||||
private const int MaxRetry = 4;
|
||||
private int _retryCount = 0;
|
||||
private bool _proxyConnected;
|
||||
private bool _destConnected;
|
||||
|
||||
@@ -160,8 +158,10 @@ namespace Shadowsocks.Controller
|
||||
private bool _remoteShutdown = false;
|
||||
private bool _closed = false;
|
||||
|
||||
private object _encryptionLock = new object();
|
||||
private object _decryptionLock = new object();
|
||||
// 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;
|
||||
@@ -207,7 +207,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
public void Close()
|
||||
{
|
||||
lock (this)
|
||||
lock (_closeConnLock)
|
||||
{
|
||||
if (_closed) return;
|
||||
_closed = true;
|
||||
@@ -218,8 +218,8 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
try
|
||||
{
|
||||
_connection?.Shutdown(SocketShutdown.Both);
|
||||
_connection?.Close();
|
||||
_connection.Shutdown(SocketShutdown.Both);
|
||||
_connection.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -227,9 +227,9 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
try
|
||||
{
|
||||
var remote = _currentRemoteSession?.Remote;
|
||||
remote?.Shutdown(SocketShutdown.Both);
|
||||
remote?.Close();
|
||||
var remote = _currentRemoteSession.Remote;
|
||||
remote.Shutdown(SocketShutdown.Both);
|
||||
remote.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -259,7 +259,7 @@ namespace Shadowsocks.Controller
|
||||
response = new byte[] { 0, 91 };
|
||||
Logging.Error("socks 5 protocol error");
|
||||
}
|
||||
_connection?.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
|
||||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
|
||||
}
|
||||
else
|
||||
Close();
|
||||
@@ -378,7 +378,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
_connection?.EndSend(ar);
|
||||
_connection.EndSend(ar);
|
||||
StartConnect();
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -420,7 +420,17 @@ namespace Shadowsocks.Controller
|
||||
EndPoint proxyEP;
|
||||
if (_config.proxy.useProxy)
|
||||
{
|
||||
remote = new Socks5Proxy();
|
||||
switch (_config.proxy.proxyType)
|
||||
{
|
||||
case ProxyConfig.PROXY_SOCKS5:
|
||||
remote = new Socks5Proxy();
|
||||
break;
|
||||
case ProxyConfig.PROXY_HTTP:
|
||||
remote = new HttpProxy();
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Unknown forward proxy.");
|
||||
}
|
||||
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort);
|
||||
}
|
||||
else
|
||||
@@ -469,7 +479,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
Logging.Info($"Proxy {proxy.ProxyEndPoint} timed out");
|
||||
proxy.Close();
|
||||
RetryConnect();
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ProxyConnectCallback(IAsyncResult ar)
|
||||
@@ -522,7 +532,7 @@ namespace Shadowsocks.Controller
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
RetryConnect();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,19 +554,7 @@ namespace Shadowsocks.Controller
|
||||
strategy?.SetFailure(server);
|
||||
Logging.Info($"{server.FriendlyName()} timed out");
|
||||
session.Remote.Close();
|
||||
RetryConnect();
|
||||
}
|
||||
|
||||
private void RetryConnect()
|
||||
{
|
||||
if (_retryCount < MaxRetry)
|
||||
{
|
||||
Logging.Debug($"Connection failed, retry ({_retryCount})");
|
||||
StartConnect();
|
||||
_retryCount++;
|
||||
}
|
||||
else
|
||||
Close();
|
||||
Close();
|
||||
}
|
||||
|
||||
private void ConnectCallback(IAsyncResult ar)
|
||||
@@ -573,7 +571,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
var remote = session.Remote;
|
||||
// Complete the connection.
|
||||
remote?.EndConnectDest(ar);
|
||||
remote.EndConnectDest(ar);
|
||||
|
||||
_destConnected = true;
|
||||
|
||||
@@ -600,7 +598,7 @@ namespace Shadowsocks.Controller
|
||||
strategy?.SetFailure(_server);
|
||||
}
|
||||
Logging.LogUsefulException(e);
|
||||
RetryConnect();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,7 +609,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
_startReceivingTime = DateTime.Now;
|
||||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
|
||||
_connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
|
||||
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
|
||||
new AsyncSession<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -636,7 +634,6 @@ namespace Shadowsocks.Controller
|
||||
int bytesToSend;
|
||||
lock (_decryptionLock)
|
||||
{
|
||||
if (_closed) return;
|
||||
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
|
||||
}
|
||||
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
|
||||
@@ -662,7 +659,6 @@ namespace Shadowsocks.Controller
|
||||
if (_closed) return;
|
||||
try
|
||||
{
|
||||
if(_connection == null) return;
|
||||
int bytesRead = _connection.EndReceive(ar);
|
||||
_totalWrite += bytesRead;
|
||||
|
||||
@@ -710,7 +706,6 @@ namespace Shadowsocks.Controller
|
||||
int bytesToSend;
|
||||
lock (_encryptionLock)
|
||||
{
|
||||
if (_closed) return;
|
||||
_encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend);
|
||||
}
|
||||
_tcprelay.UpdateOutboundCounter(_server, bytesToSend);
|
||||
@@ -740,7 +735,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
var session = (AsyncSession)ar.AsyncState;
|
||||
session.Remote.EndSend(ar);
|
||||
_connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
|
||||
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -751,11 +746,10 @@ namespace Shadowsocks.Controller
|
||||
|
||||
private void PipeConnectionSendCallback(IAsyncResult ar)
|
||||
{
|
||||
if (_closed) return;
|
||||
try
|
||||
{
|
||||
var session = (AsyncSession)ar.AsyncState;
|
||||
_connection?.EndSend(ar);
|
||||
_connection.EndSend(ar);
|
||||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Shadowsocks.Controller
|
||||
public string LatestVersionLocalName;
|
||||
public event EventHandler CheckUpdateCompleted;
|
||||
|
||||
public const string Version = "3.3.1";
|
||||
public const string Version = "3.3.2";
|
||||
|
||||
private class CheckUpdateTimer : System.Timers.Timer
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Shadowsocks.Controller
|
||||
private PACServer _pacServer;
|
||||
private Configuration _config;
|
||||
private StrategyManager _strategyManager;
|
||||
private PolipoRunner polipoRunner;
|
||||
private PrivoxyRunner privoxyRunner;
|
||||
private GFWListUpdater gfwListUpdater;
|
||||
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance;
|
||||
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }
|
||||
@@ -221,9 +221,10 @@ namespace Shadowsocks.Controller
|
||||
SaveConfig(_config);
|
||||
}
|
||||
|
||||
public void EnableProxy(string proxy, int port)
|
||||
public void EnableProxy(int type, string proxy, int port)
|
||||
{
|
||||
_config.proxy.useProxy = true;
|
||||
_config.proxy.proxyType = type;
|
||||
_config.proxy.proxyServer = proxy;
|
||||
_config.proxy.proxyPort = port;
|
||||
SaveConfig(_config);
|
||||
@@ -263,9 +264,9 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
_listener.Stop();
|
||||
}
|
||||
if (polipoRunner != null)
|
||||
if (privoxyRunner != null)
|
||||
{
|
||||
polipoRunner.Stop();
|
||||
privoxyRunner.Stop();
|
||||
}
|
||||
if (_config.enabled)
|
||||
{
|
||||
@@ -416,9 +417,9 @@ namespace Shadowsocks.Controller
|
||||
_config = Configuration.Load();
|
||||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
|
||||
|
||||
if (polipoRunner == null)
|
||||
if (privoxyRunner == null)
|
||||
{
|
||||
polipoRunner = new PolipoRunner();
|
||||
privoxyRunner = new PrivoxyRunner();
|
||||
}
|
||||
if (_pacServer == null)
|
||||
{
|
||||
@@ -440,11 +441,11 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
_listener.Stop();
|
||||
}
|
||||
// don't put polipoRunner.Start() before pacServer.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
|
||||
polipoRunner.Stop();
|
||||
privoxyRunner.Stop();
|
||||
try
|
||||
{
|
||||
var strategy = GetCurrentStrategy();
|
||||
@@ -453,7 +454,7 @@ namespace Shadowsocks.Controller
|
||||
strategy.ReloadServers();
|
||||
}
|
||||
|
||||
polipoRunner.Start(_config);
|
||||
privoxyRunner.Start(_config);
|
||||
|
||||
TCPRelay tcpRelay = new TCPRelay(this, _config);
|
||||
UDPRelay udpRelay = new UDPRelay(this);
|
||||
@@ -461,7 +462,7 @@ namespace Shadowsocks.Controller
|
||||
services.Add(tcpRelay);
|
||||
services.Add(udpRelay);
|
||||
services.Add(_pacServer);
|
||||
services.Add(new PortForwarder(polipoRunner.RunningPort));
|
||||
services.Add(new PortForwarder(privoxyRunner.RunningPort));
|
||||
_listener = new Listener(services);
|
||||
_listener.Start(_config);
|
||||
}
|
||||
|
||||
@@ -39,8 +39,10 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
if (runKey != null)
|
||||
{
|
||||
try { runKey.Close(); }
|
||||
catch (Exception e)
|
||||
try {
|
||||
runKey.Close();
|
||||
runKey.Dispose();
|
||||
} catch (Exception e)
|
||||
{ Logging.LogUsefulException(e); }
|
||||
}
|
||||
}
|
||||
@@ -84,8 +86,10 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
if (runKey != null)
|
||||
{
|
||||
try { runKey.Close(); }
|
||||
catch (Exception e)
|
||||
try {
|
||||
runKey.Close();
|
||||
runKey.Dispose();
|
||||
} catch (Exception e)
|
||||
{ Logging.LogUsefulException(e); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +84,10 @@ namespace Shadowsocks.Controller
|
||||
MessageBox.Show( I18N.GetString( "Failed to update registry" ) );
|
||||
} finally {
|
||||
if ( registry != null ) {
|
||||
try { registry.Close(); }
|
||||
catch (Exception e)
|
||||
try {
|
||||
registry.Close();
|
||||
registry.Dispose();
|
||||
} catch (Exception e)
|
||||
{ Logging.LogUsefulException(e); }
|
||||
}
|
||||
}
|
||||
@@ -119,8 +121,10 @@ namespace Shadowsocks.Controller
|
||||
Logging.LogUsefulException( e );
|
||||
} finally {
|
||||
if ( registry != null ) {
|
||||
try { registry.Close(); }
|
||||
catch (Exception e)
|
||||
try {
|
||||
registry.Close();
|
||||
registry.Dispose();
|
||||
} catch (Exception e)
|
||||
{ Logging.LogUsefulException(e); }
|
||||
}
|
||||
}
|
||||
@@ -165,8 +169,10 @@ namespace Shadowsocks.Controller
|
||||
} finally {
|
||||
if (registry != null)
|
||||
{
|
||||
try { registry.Close(); }
|
||||
catch (Exception e)
|
||||
try {
|
||||
registry.Close();
|
||||
registry.Dispose();
|
||||
} catch (Exception e)
|
||||
{ Logging.LogUsefulException(e); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ Servers=服务器
|
||||
Edit Servers...=编辑服务器...
|
||||
Statistics Config...=统计配置...
|
||||
Start on Boot=开机启动
|
||||
SOCKS5 Proxy...=SOCKS5代理设置...
|
||||
Forward Proxy...=正向代理设置...
|
||||
Allow Clients from LAN=允许来自局域网的连接
|
||||
Local PAC=使用本地 PAC
|
||||
Online PAC=使用在线 PAC
|
||||
@@ -58,6 +58,7 @@ Move D&own=下移(&O)
|
||||
|
||||
Edit Proxy=代理设置
|
||||
Use Proxy=使用代理
|
||||
Proxy Type=代理类型
|
||||
Proxy Addr=代理地址
|
||||
Proxy Port=代理端口
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
listen-address __POLIPO_BIND_IP__:__POLIPO_BIND_PORT__
|
||||
listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__
|
||||
show-on-task-bar 0
|
||||
activity-animation 0
|
||||
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
|
||||
|
||||
@@ -12,7 +12,7 @@ Servers=伺服器
|
||||
Edit Servers...=編輯伺服器...
|
||||
Statistics Config...=統計配置...
|
||||
Start on Boot=開機啟動
|
||||
SOCKS5 Proxy...=SOCKS5代理設置...
|
||||
Forward Proxy...=正向代理設置...
|
||||
Allow Clients from LAN=允許來自區域網路的連接
|
||||
Local PAC=使用本地 PAC
|
||||
Online PAC=使用在線 PAC
|
||||
@@ -58,6 +58,7 @@ Move D&own=下移(&O)
|
||||
|
||||
Edit Proxy=代理設置
|
||||
Use Proxy=使用代理
|
||||
Proxy Type=代理類別
|
||||
Proxy Addr=代理位址
|
||||
Proxy Port=代理連接埠
|
||||
|
||||
|
||||
@@ -4,17 +4,27 @@ namespace Shadowsocks.Encryption
|
||||
{
|
||||
public struct EncryptorInfo
|
||||
{
|
||||
public string name;
|
||||
public int key_size;
|
||||
public int iv_size;
|
||||
public int type;
|
||||
public int KeySize;
|
||||
public int IvSize;
|
||||
public int Type;
|
||||
public string InnerLibName;
|
||||
|
||||
public EncryptorInfo(string name, int key_size, int iv_size, int type)
|
||||
// For those who make use of internal crypto method name
|
||||
// e.g. mbed TLS
|
||||
public EncryptorInfo(string innerLibName, int keySize, int ivSize, int type)
|
||||
{
|
||||
this.name = name;
|
||||
this.key_size = key_size;
|
||||
this.iv_size = iv_size;
|
||||
this.type = type;
|
||||
this.KeySize = keySize;
|
||||
this.IvSize = ivSize;
|
||||
this.Type = type;
|
||||
this.InnerLibName = innerLibName;
|
||||
}
|
||||
|
||||
public EncryptorInfo(int keySize, int ivSize, int type)
|
||||
{
|
||||
this.KeySize = keySize;
|
||||
this.IvSize = ivSize;
|
||||
this.Type = type;
|
||||
this.InnerLibName = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ namespace Shadowsocks.Encryption
|
||||
protected bool _encryptIVSent;
|
||||
protected string _method;
|
||||
protected int _cipher;
|
||||
// cipher name in MbedTLS, useless when using LibSodium
|
||||
protected string _cipherMbedName;
|
||||
protected EncryptorInfo _cipherInfo;
|
||||
// internal name in the crypto library
|
||||
protected string _innerLibName;
|
||||
protected EncryptorInfo CipherInfo;
|
||||
protected byte[] _key;
|
||||
protected int keyLen;
|
||||
protected int ivLen;
|
||||
@@ -46,15 +46,15 @@ namespace Shadowsocks.Encryption
|
||||
_method = method;
|
||||
string k = method + ":" + password;
|
||||
ciphers = getCiphers();
|
||||
_cipherInfo = ciphers[_method];
|
||||
_cipherMbedName = _cipherInfo.name;
|
||||
_cipher = _cipherInfo.type;
|
||||
CipherInfo = ciphers[_method];
|
||||
_innerLibName = CipherInfo.InnerLibName;
|
||||
_cipher = CipherInfo.Type;
|
||||
if (_cipher == 0)
|
||||
{
|
||||
throw new Exception("method not found");
|
||||
}
|
||||
keyLen = _cipherInfo.key_size;
|
||||
ivLen = _cipherInfo.iv_size;
|
||||
keyLen = CipherInfo.KeySize;
|
||||
ivLen = CipherInfo.IvSize;
|
||||
_key = CachedKeys.GetOrAdd(k, (nk) =>
|
||||
{
|
||||
byte[] passbuf = Encoding.UTF8.GetBytes(password);
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Shadowsocks.Encryption
|
||||
realkey = _key;
|
||||
}
|
||||
MbedTLS.cipher_init(ctx);
|
||||
if (MbedTLS.cipher_setup( ctx, MbedTLS.cipher_info_from_string( _cipherMbedName ) ) != 0 )
|
||||
if (MbedTLS.cipher_setup( ctx, MbedTLS.cipher_info_from_string( _innerLibName ) ) != 0 )
|
||||
throw new Exception("Cannot initialize mbed TLS cipher context");
|
||||
/*
|
||||
* MbedTLS takes key length by bit
|
||||
@@ -105,8 +105,12 @@ namespace Shadowsocks.Encryption
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
// instance based lock
|
||||
private readonly object _lock = new object();
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
@@ -120,31 +124,32 @@ namespace Shadowsocks.Encryption
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
lock (this)
|
||||
lock (_lock)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
if (_encryptCtx != IntPtr.Zero)
|
||||
{
|
||||
MbedTLS.cipher_free(_encryptCtx);
|
||||
Marshal.FreeHGlobal(_encryptCtx);
|
||||
_encryptCtx = IntPtr.Zero;
|
||||
}
|
||||
if (_decryptCtx != IntPtr.Zero)
|
||||
{
|
||||
MbedTLS.cipher_free(_decryptCtx);
|
||||
Marshal.FreeHGlobal(_decryptCtx);
|
||||
_decryptCtx = IntPtr.Zero;
|
||||
}
|
||||
// free managed objects
|
||||
}
|
||||
|
||||
// free unmanaged objects
|
||||
if (_encryptCtx != IntPtr.Zero)
|
||||
{
|
||||
MbedTLS.cipher_free(_encryptCtx);
|
||||
Marshal.FreeHGlobal(_encryptCtx);
|
||||
_encryptCtx = IntPtr.Zero;
|
||||
}
|
||||
if (_decryptCtx != IntPtr.Zero)
|
||||
{
|
||||
MbedTLS.cipher_free(_decryptCtx);
|
||||
Marshal.FreeHGlobal(_decryptCtx);
|
||||
_decryptCtx = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,22 +12,24 @@ namespace Shadowsocks.Encryption
|
||||
|
||||
const int SODIUM_BLOCK_SIZE = 64;
|
||||
|
||||
static byte[] sodiumBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
|
||||
|
||||
protected int _encryptBytesRemaining;
|
||||
protected int _decryptBytesRemaining;
|
||||
protected ulong _encryptIC;
|
||||
protected ulong _decryptIC;
|
||||
protected byte[] _encryptBuf;
|
||||
protected byte[] _decryptBuf;
|
||||
|
||||
public SodiumEncryptor(string method, string password, bool onetimeauth, bool isudp)
|
||||
: base(method, password, onetimeauth, isudp)
|
||||
{
|
||||
_encryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
|
||||
_decryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
|
||||
}
|
||||
|
||||
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> {
|
||||
{ "salsa20", new EncryptorInfo("salsa20", 32, 8, CIPHER_SALSA20) },
|
||||
{ "chacha20", new EncryptorInfo("chacha20", 32, 8, CIPHER_CHACHA20) },
|
||||
{ "chacha20-ietf", new EncryptorInfo("chacha20-ietf", 32, 12, CIPHER_CHACHA20_IETF) }
|
||||
{ "salsa20", new EncryptorInfo(32, 8, CIPHER_SALSA20) },
|
||||
{ "chacha20", new EncryptorInfo(32, 8, CIPHER_CHACHA20) },
|
||||
{ "chacha20-ietf", new EncryptorInfo(32, 12, CIPHER_CHACHA20_IETF) }
|
||||
};
|
||||
|
||||
protected override Dictionary<string, EncryptorInfo> getCiphers()
|
||||
@@ -45,54 +47,52 @@ namespace Shadowsocks.Encryption
|
||||
// TODO write a unidirection cipher so we don't have to if if if
|
||||
int bytesRemaining;
|
||||
ulong ic;
|
||||
byte[] sodiumBuf;
|
||||
byte[] iv;
|
||||
|
||||
// I'm tired. just add a big lock
|
||||
// let's optimize for RAM instead of CPU
|
||||
lock(sodiumBuf)
|
||||
if (isCipher)
|
||||
{
|
||||
if (isCipher)
|
||||
{
|
||||
bytesRemaining = _encryptBytesRemaining;
|
||||
ic = _encryptIC;
|
||||
iv = _encryptIV;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesRemaining = _decryptBytesRemaining;
|
||||
ic = _decryptIC;
|
||||
iv = _decryptIV;
|
||||
}
|
||||
int padding = bytesRemaining;
|
||||
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length);
|
||||
bytesRemaining = _encryptBytesRemaining;
|
||||
ic = _encryptIC;
|
||||
sodiumBuf = _encryptBuf;
|
||||
iv = _encryptIV;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesRemaining = _decryptBytesRemaining;
|
||||
ic = _decryptIC;
|
||||
sodiumBuf = _decryptBuf;
|
||||
iv = _decryptIV;
|
||||
}
|
||||
int padding = bytesRemaining;
|
||||
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length);
|
||||
|
||||
switch (_cipher)
|
||||
{
|
||||
case CIPHER_SALSA20:
|
||||
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
|
||||
break;
|
||||
case CIPHER_CHACHA20:
|
||||
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
|
||||
break;
|
||||
case CIPHER_CHACHA20_IETF:
|
||||
Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key);
|
||||
break;
|
||||
}
|
||||
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
|
||||
padding += length;
|
||||
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
|
||||
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
|
||||
switch (_cipher)
|
||||
{
|
||||
case CIPHER_SALSA20:
|
||||
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
|
||||
break;
|
||||
case CIPHER_CHACHA20:
|
||||
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
|
||||
break;
|
||||
case CIPHER_CHACHA20_IETF:
|
||||
Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key);
|
||||
break;
|
||||
}
|
||||
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
|
||||
padding += length;
|
||||
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
|
||||
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
|
||||
|
||||
if (isCipher)
|
||||
{
|
||||
_encryptBytesRemaining = bytesRemaining;
|
||||
_encryptIC = ic;
|
||||
}
|
||||
else
|
||||
{
|
||||
_decryptBytesRemaining = bytesRemaining;
|
||||
_decryptIC = ic;
|
||||
}
|
||||
if (isCipher)
|
||||
{
|
||||
_encryptBytesRemaining = bytesRemaining;
|
||||
_encryptIC = ic;
|
||||
}
|
||||
else
|
||||
{
|
||||
_decryptBytesRemaining = bytesRemaining;
|
||||
_decryptIC = ic;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,11 @@ namespace Shadowsocks.Model
|
||||
config.proxy = new ProxyConfig();
|
||||
if (config.hotkey == null)
|
||||
config.hotkey = new HotkeyConfig();
|
||||
|
||||
if (config.proxy.proxyType < ProxyConfig.PROXY_SOCKS5 || config.proxy.proxyType > ProxyConfig.PROXY_HTTP)
|
||||
{
|
||||
config.proxy.proxyType = ProxyConfig.PROXY_SOCKS5;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
using Shadowsocks.View;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using System;
|
||||
|
||||
namespace Shadowsocks.Model
|
||||
{
|
||||
[Serializable]
|
||||
public class ProxyConfig
|
||||
{
|
||||
public const int PROXY_SOCKS5 = 0;
|
||||
public const int PROXY_HTTP = 1;
|
||||
|
||||
public bool useProxy;
|
||||
public int proxyType;
|
||||
public string proxyServer;
|
||||
public int proxyPort;
|
||||
|
||||
public ProxyConfig()
|
||||
{
|
||||
useProxy = false;
|
||||
proxyType = PROXY_SOCKS5;
|
||||
proxyServer = "";
|
||||
proxyPort = 0;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Shadowsocks.Proxy
|
||||
public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
_remote?.BeginSend(buffer, offset, size, socketFlags, callback, state);
|
||||
_remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
|
||||
}
|
||||
|
||||
public int EndSend(IAsyncResult asyncResult)
|
||||
@@ -78,7 +78,7 @@ namespace Shadowsocks.Proxy
|
||||
public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
_remote?.BeginReceive(buffer, offset, size, socketFlags, callback, state);
|
||||
_remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
|
||||
}
|
||||
|
||||
public int EndReceive(IAsyncResult asyncResult)
|
||||
@@ -88,12 +88,12 @@ namespace Shadowsocks.Proxy
|
||||
|
||||
public void Shutdown(SocketShutdown how)
|
||||
{
|
||||
_remote?.Shutdown(how);
|
||||
_remote.Shutdown(how);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_remote?.Dispose();
|
||||
_remote.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using Shadowsocks.Controller;
|
||||
using Shadowsocks.Util.Sockets;
|
||||
|
||||
namespace Shadowsocks.Proxy
|
||||
{
|
||||
public class HttpProxy : IProxy
|
||||
{
|
||||
private class FakeAsyncResult : IAsyncResult
|
||||
{
|
||||
public readonly HttpState innerState;
|
||||
|
||||
private readonly IAsyncResult r;
|
||||
|
||||
public FakeAsyncResult(IAsyncResult orig, HttpState state)
|
||||
{
|
||||
r = orig;
|
||||
innerState = state;
|
||||
}
|
||||
|
||||
public bool IsCompleted => r.IsCompleted;
|
||||
public WaitHandle AsyncWaitHandle => r.AsyncWaitHandle;
|
||||
public object AsyncState => innerState.AsyncState;
|
||||
public bool CompletedSynchronously => r.CompletedSynchronously;
|
||||
}
|
||||
|
||||
private class HttpState
|
||||
{
|
||||
|
||||
public AsyncCallback Callback { get; set; }
|
||||
|
||||
public object AsyncState { get; set; }
|
||||
|
||||
public int BytesToRead;
|
||||
|
||||
public Exception ex { get; set; }
|
||||
}
|
||||
|
||||
public EndPoint LocalEndPoint => _remote.LocalEndPoint;
|
||||
public EndPoint ProxyEndPoint { get; private set; }
|
||||
public EndPoint DestEndPoint { get; private set; }
|
||||
|
||||
|
||||
private readonly WrappedSocket _remote = new WrappedSocket();
|
||||
|
||||
|
||||
public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
|
||||
{
|
||||
ProxyEndPoint = remoteEP;
|
||||
|
||||
_remote.BeginConnect(remoteEP, callback, state);
|
||||
}
|
||||
|
||||
public void EndConnectProxy(IAsyncResult asyncResult)
|
||||
{
|
||||
_remote.EndConnect(asyncResult);
|
||||
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
||||
}
|
||||
|
||||
private const string HTTP_CRLF = "\r\n";
|
||||
private const string HTTP_CONNECT_TEMPLATE =
|
||||
"CONNECT {0} HTTP/1.1" + HTTP_CRLF +
|
||||
"Host: {0}" + HTTP_CRLF +
|
||||
"Proxy-Connection: keep-alive" + HTTP_CRLF +
|
||||
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" + HTTP_CRLF +
|
||||
"" + HTTP_CRLF; // End with an empty line
|
||||
|
||||
public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state)
|
||||
{
|
||||
DestEndPoint = destEndPoint;
|
||||
string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint);
|
||||
|
||||
var b = Encoding.UTF8.GetBytes(request);
|
||||
|
||||
var st = new HttpState();
|
||||
st.Callback = callback;
|
||||
st.AsyncState = state;
|
||||
|
||||
_remote.BeginSend(b, 0, b.Length, 0, HttpRequestSendCallback, st);
|
||||
}
|
||||
|
||||
public void EndConnectDest(IAsyncResult asyncResult)
|
||||
{
|
||||
var state = ((FakeAsyncResult)asyncResult).innerState;
|
||||
|
||||
if (state.ex != null)
|
||||
{
|
||||
throw state.ex;
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
_remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
|
||||
}
|
||||
|
||||
public int EndSend(IAsyncResult asyncResult)
|
||||
{
|
||||
return _remote.EndSend(asyncResult);
|
||||
}
|
||||
|
||||
public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
_remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
|
||||
}
|
||||
|
||||
public int EndReceive(IAsyncResult asyncResult)
|
||||
{
|
||||
return _remote.EndReceive(asyncResult);
|
||||
}
|
||||
|
||||
public void Shutdown(SocketShutdown how)
|
||||
{
|
||||
_remote.Shutdown(how);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_remote.Dispose();
|
||||
}
|
||||
|
||||
private void HttpRequestSendCallback(IAsyncResult ar)
|
||||
{
|
||||
var state = (HttpState) ar.AsyncState;
|
||||
try
|
||||
{
|
||||
_remote.EndSend(ar);
|
||||
|
||||
// start line read
|
||||
new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state.ex = ex;
|
||||
state.Callback?.Invoke(new FakeAsyncResult(ar, state));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFinish(byte[] lastBytes, int index, int length, object state)
|
||||
{
|
||||
var st = (FakeAsyncResult)state;
|
||||
|
||||
if (st.innerState.ex == null)
|
||||
{
|
||||
if (!_established)
|
||||
{
|
||||
st.innerState.ex = new Exception(I18N.GetString("Proxy request failed"));
|
||||
}
|
||||
// TODO: save last bytes
|
||||
}
|
||||
st.innerState.Callback?.Invoke(st);
|
||||
}
|
||||
|
||||
private void OnException(Exception ex, object state)
|
||||
{
|
||||
var st = (FakeAsyncResult) state;
|
||||
|
||||
st.innerState.ex = ex;
|
||||
}
|
||||
|
||||
private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$");
|
||||
private int _respondLineCount = 0;
|
||||
private bool _established = false;
|
||||
|
||||
private bool OnLineRead(string line, object state)
|
||||
{
|
||||
Logging.Debug(line);
|
||||
|
||||
if (_respondLineCount == 0)
|
||||
{
|
||||
var m = HttpRespondHeaderRegex.Match(line);
|
||||
if (m.Success)
|
||||
{
|
||||
var resultCode = m.Groups[2].Value;
|
||||
if ("200" != resultCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
_established = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line.IsNullOrEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_respondLineCount++;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ namespace Shadowsocks.Proxy
|
||||
public Exception ex { get; set; }
|
||||
}
|
||||
|
||||
private WrappedSocket _remote = new WrappedSocket();
|
||||
private readonly WrappedSocket _remote = new WrappedSocket();
|
||||
|
||||
private const int Socks5PktMaxSize = 4 + 16 + 2;
|
||||
private readonly byte[] _receiveBuffer = new byte[Socks5PktMaxSize];
|
||||
@@ -124,7 +124,7 @@ namespace Shadowsocks.Proxy
|
||||
st.Callback = callback;
|
||||
st.AsyncState = state;
|
||||
|
||||
_remote?.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, st);
|
||||
_remote.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, st);
|
||||
}
|
||||
|
||||
public void EndConnectDest(IAsyncResult asyncResult)
|
||||
@@ -140,7 +140,7 @@ namespace Shadowsocks.Proxy
|
||||
public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
_remote?.BeginSend(buffer, offset, size, socketFlags, callback, state);
|
||||
_remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
|
||||
}
|
||||
|
||||
public int EndSend(IAsyncResult asyncResult)
|
||||
@@ -151,7 +151,7 @@ namespace Shadowsocks.Proxy
|
||||
public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
_remote?.BeginReceive(buffer, offset, size, socketFlags, callback, state);
|
||||
_remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
|
||||
}
|
||||
|
||||
public int EndReceive(IAsyncResult asyncResult)
|
||||
@@ -161,12 +161,12 @@ namespace Shadowsocks.Proxy
|
||||
|
||||
public void Shutdown(SocketShutdown how)
|
||||
{
|
||||
_remote?.Shutdown(how);
|
||||
_remote.Shutdown(how);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_remote?.Dispose();
|
||||
_remote.Dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,11 @@ namespace Shadowsocks.Util
|
||||
_hotKeyManager.KeyPressed += HotKeyManagerPressed;
|
||||
}
|
||||
|
||||
public static void Destroy() => _hotKeyManager.Dispose();
|
||||
public static void Destroy()
|
||||
{
|
||||
_hotKeyManager.KeyPressed -= HotKeyManagerPressed;
|
||||
_hotKeyManager.Dispose();
|
||||
}
|
||||
|
||||
private static void HotKeyManagerPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
@@ -79,7 +83,8 @@ namespace Shadowsocks.Util
|
||||
}
|
||||
}
|
||||
|
||||
public static HotKey Str2HotKey( string s ) {
|
||||
public static HotKey Str2HotKey(string s)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (s.IsNullOrEmpty()) return null;
|
||||
|
||||
@@ -12,25 +12,25 @@ namespace Shadowsocks.Util.ProcessManagement
|
||||
public class Job : IDisposable
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
static extern IntPtr CreateJobObject(IntPtr a, string lpName);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
|
||||
private static extern IntPtr CreateJobObject(IntPtr a, string lpName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
|
||||
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)]
|
||||
static extern bool CloseHandle(IntPtr hObject);
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
private IntPtr handle;
|
||||
private IntPtr handle = IntPtr.Zero;
|
||||
private bool disposed;
|
||||
|
||||
public Job()
|
||||
{
|
||||
handle = CreateJobObject(IntPtr.Zero, null);
|
||||
|
||||
var extendedInfoPtr = IntPtr.Zero;
|
||||
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||
{
|
||||
LimitFlags = 0x2000
|
||||
@@ -41,12 +41,25 @@ namespace Shadowsocks.Util.ProcessManagement
|
||||
BasicLimitInformation = info
|
||||
};
|
||||
|
||||
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
||||
IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
|
||||
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
|
||||
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()));
|
||||
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 void Dispose()
|
||||
@@ -57,19 +70,26 @@ namespace Shadowsocks.Util.ProcessManagement
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
|
||||
if (disposing) { }
|
||||
|
||||
Close();
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
private void Close()
|
||||
{
|
||||
CloseHandle(handle);
|
||||
handle = IntPtr.Zero;
|
||||
if (handle != IntPtr.Zero)
|
||||
{
|
||||
CloseHandle(handle);
|
||||
handle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
~Job()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public bool AddProcess(IntPtr processHandle)
|
||||
@@ -78,8 +98,7 @@ namespace Shadowsocks.Util.ProcessManagement
|
||||
|
||||
if (!succ)
|
||||
{
|
||||
var err = Marshal.GetLastWin32Error();
|
||||
Logging.Error("Failed to call AssignProcessToJobObject! GetLastError=" + err);
|
||||
Logging.Error("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
return succ;
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Shadowsocks.Util.Sockets
|
||||
{
|
||||
public class LineReader
|
||||
{
|
||||
private readonly WrappedSocket _socket;
|
||||
private readonly Func<string, object, bool> _onLineRead;
|
||||
private readonly Action<Exception, object> _onException;
|
||||
private readonly Action<byte[], int, int, object> _onFinish;
|
||||
private readonly Encoding _encoding;
|
||||
// private readonly string _delimiter;
|
||||
private readonly byte[] _delimiterBytes;
|
||||
private readonly int[] _delimiterSearchCharTable;
|
||||
private readonly int[] _delimiterSearchOffsetTable;
|
||||
private readonly object _state;
|
||||
|
||||
private readonly byte[] _lineBuffer;
|
||||
|
||||
private int _bufferIndex;
|
||||
|
||||
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 (socket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(socket));
|
||||
}
|
||||
if (onLineRead == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(onLineRead));
|
||||
}
|
||||
if (encoding == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(encoding));
|
||||
}
|
||||
if (delimiter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(delimiter));
|
||||
}
|
||||
|
||||
_socket = socket;
|
||||
_onLineRead = onLineRead;
|
||||
_onException = onException;
|
||||
_onFinish = onFinish;
|
||||
_encoding = encoding;
|
||||
// _delimiter = delimiter;
|
||||
_state = state;
|
||||
|
||||
// decode delimiter
|
||||
_delimiterBytes = encoding.GetBytes(delimiter);
|
||||
|
||||
if (_delimiterBytes.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Too short!", nameof(delimiter));
|
||||
}
|
||||
|
||||
if (maxLineBytes < _delimiterBytes.Length)
|
||||
{
|
||||
throw new ArgumentException("Too small!", nameof(maxLineBytes));
|
||||
}
|
||||
|
||||
_delimiterSearchCharTable = MakeCharTable(_delimiterBytes);
|
||||
_delimiterSearchOffsetTable = MakeOffsetTable(_delimiterBytes);
|
||||
|
||||
_lineBuffer = new byte[maxLineBytes];
|
||||
|
||||
// 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(length);
|
||||
return;
|
||||
}
|
||||
|
||||
length += bytesRead;
|
||||
|
||||
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(length);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnException(Exception ex)
|
||||
{
|
||||
_onException?.Invoke(ex, _state);
|
||||
}
|
||||
|
||||
private void OnFinish(int length)
|
||||
{
|
||||
_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
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,9 @@ namespace Shadowsocks.View
|
||||
const int BACK_OFFSET = 65536;
|
||||
ShadowsocksController controller;
|
||||
|
||||
// global traffic update lock, make it static
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
#region chart
|
||||
long lastMaxSpeed;
|
||||
ShadowsocksController.QueueLast<TrafficInfo> traffic = new ShadowsocksController.QueueLast<TrafficInfo>();
|
||||
@@ -71,7 +74,7 @@ namespace Shadowsocks.View
|
||||
long maxSpeed = 0;
|
||||
long lastInbound, lastOutbound;
|
||||
|
||||
lock (this)
|
||||
lock (_lock)
|
||||
{
|
||||
if (traffic.Count == 0)
|
||||
return;
|
||||
@@ -120,7 +123,7 @@ namespace Shadowsocks.View
|
||||
|
||||
private void controller_TrafficChanged(object sender, EventArgs e)
|
||||
{
|
||||
lock (this)
|
||||
lock (_lock)
|
||||
{
|
||||
traffic = new ShadowsocksController.QueueLast<TrafficInfo>();
|
||||
foreach (var trafficPerSecond in controller.traffic)
|
||||
|
||||
@@ -271,7 +271,7 @@ namespace Shadowsocks.View
|
||||
this.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)),
|
||||
this.editOnlinePACItem = CreateMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)),
|
||||
}),
|
||||
this.proxyItem = CreateMenuItem("SOCKS5 Proxy...", new EventHandler(this.proxyItem_Click)),
|
||||
this.proxyItem = CreateMenuItem("Forward Proxy...", new EventHandler(this.proxyItem_Click)),
|
||||
new MenuItem("-"),
|
||||
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
|
||||
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
|
||||
@@ -501,12 +501,14 @@ namespace Shadowsocks.View
|
||||
|
||||
void logForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
logForm.Dispose();
|
||||
logForm = null;
|
||||
Utils.ReleaseMemory(true);
|
||||
}
|
||||
|
||||
void configForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
configForm.Dispose();
|
||||
configForm = null;
|
||||
Utils.ReleaseMemory(true);
|
||||
if (_isFirstRun)
|
||||
@@ -519,12 +521,14 @@ namespace Shadowsocks.View
|
||||
|
||||
void proxyForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
proxyForm.Dispose();
|
||||
proxyForm = null;
|
||||
Utils.ReleaseMemory(true);
|
||||
}
|
||||
|
||||
void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
hotkeySettingsForm.Dispose();
|
||||
hotkeySettingsForm = null;
|
||||
Utils.ReleaseMemory(true);
|
||||
}
|
||||
@@ -564,11 +568,7 @@ namespace Shadowsocks.View
|
||||
|
||||
private void notifyIcon1_Click(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
// TODO: show something interesting
|
||||
}
|
||||
else if (e.Button == MouseButtons.Middle)
|
||||
if ( e.Button == MouseButtons.Middle )
|
||||
{
|
||||
ShowLogForm();
|
||||
}
|
||||
|
||||
+58
-7
@@ -38,9 +38,13 @@
|
||||
this.ProxyServerTextBox = new System.Windows.Forms.TextBox();
|
||||
this.ProxyPortLable = new System.Windows.Forms.Label();
|
||||
this.ProxyPortTextBox = new System.Windows.Forms.TextBox();
|
||||
this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.ProxyTypeLabel = new System.Windows.Forms.Label();
|
||||
this.ProxyTypeComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.tableLayoutPanel3.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
this.tableLayoutPanel4.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
@@ -49,16 +53,18 @@
|
||||
this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.tableLayoutPanel1.ColumnCount = 1;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 0, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.UseProxyCheckBox, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel4, 0, 1);
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(15, 15);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 3;
|
||||
this.tableLayoutPanel1.RowCount = 4;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(395, 87);
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(395, 123);
|
||||
this.tableLayoutPanel1.TabIndex = 0;
|
||||
//
|
||||
// tableLayoutPanel3
|
||||
@@ -72,7 +78,7 @@
|
||||
this.tableLayoutPanel3.Controls.Add(this.MyCancelButton, 1, 0);
|
||||
this.tableLayoutPanel3.Controls.Add(this.OKButton, 0, 0);
|
||||
this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Right;
|
||||
this.tableLayoutPanel3.Location = new System.Drawing.Point(236, 58);
|
||||
this.tableLayoutPanel3.Location = new System.Drawing.Point(236, 94);
|
||||
this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3);
|
||||
this.tableLayoutPanel3.Name = "tableLayoutPanel3";
|
||||
this.tableLayoutPanel3.RowCount = 1;
|
||||
@@ -129,10 +135,11 @@
|
||||
this.tableLayoutPanel2.Controls.Add(this.ProxyServerTextBox, 1, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.ProxyPortLable, 2, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.ProxyPortTextBox, 3, 0);
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 25);
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 61);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
this.tableLayoutPanel2.RowCount = 1;
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(389, 27);
|
||||
this.tableLayoutPanel2.TabIndex = 1;
|
||||
//
|
||||
@@ -174,6 +181,45 @@
|
||||
this.ProxyPortTextBox.TabIndex = 3;
|
||||
this.ProxyPortTextBox.WordWrap = false;
|
||||
//
|
||||
// tableLayoutPanel4
|
||||
//
|
||||
this.tableLayoutPanel4.AutoSize = true;
|
||||
this.tableLayoutPanel4.ColumnCount = 2;
|
||||
this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel4.Controls.Add(this.ProxyTypeLabel, 0, 0);
|
||||
this.tableLayoutPanel4.Controls.Add(this.ProxyTypeComboBox, 1, 0);
|
||||
this.tableLayoutPanel4.Location = new System.Drawing.Point(3, 25);
|
||||
this.tableLayoutPanel4.Name = "tableLayoutPanel4";
|
||||
this.tableLayoutPanel4.RowCount = 1;
|
||||
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel4.Size = new System.Drawing.Size(198, 30);
|
||||
this.tableLayoutPanel4.TabIndex = 10;
|
||||
//
|
||||
// ProxyTypeLabel
|
||||
//
|
||||
this.ProxyTypeLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.ProxyTypeLabel.AutoSize = true;
|
||||
this.ProxyTypeLabel.Location = new System.Drawing.Point(3, 9);
|
||||
this.ProxyTypeLabel.Name = "ProxyTypeLabel";
|
||||
this.ProxyTypeLabel.Size = new System.Drawing.Size(65, 12);
|
||||
this.ProxyTypeLabel.TabIndex = 1;
|
||||
this.ProxyTypeLabel.Text = "Proxy Type";
|
||||
//
|
||||
// ProxyTypeComboBox
|
||||
//
|
||||
this.ProxyTypeComboBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ProxyTypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.ProxyTypeComboBox.FormattingEnabled = true;
|
||||
this.ProxyTypeComboBox.Items.AddRange(new object[] {
|
||||
"SOCKS5",
|
||||
"HTTP"});
|
||||
this.ProxyTypeComboBox.Location = new System.Drawing.Point(74, 5);
|
||||
this.ProxyTypeComboBox.Margin = new System.Windows.Forms.Padding(3, 5, 3, 5);
|
||||
this.ProxyTypeComboBox.Name = "ProxyTypeComboBox";
|
||||
this.ProxyTypeComboBox.Size = new System.Drawing.Size(121, 20);
|
||||
this.ProxyTypeComboBox.TabIndex = 2;
|
||||
//
|
||||
// ProxyForm
|
||||
//
|
||||
this.AcceptButton = this.OKButton;
|
||||
@@ -182,7 +228,7 @@
|
||||
this.AutoSize = true;
|
||||
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.CancelButton = this.MyCancelButton;
|
||||
this.ClientSize = new System.Drawing.Size(441, 149);
|
||||
this.ClientSize = new System.Drawing.Size(441, 165);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.MaximizeBox = false;
|
||||
@@ -197,6 +243,8 @@
|
||||
this.tableLayoutPanel3.ResumeLayout(false);
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
this.tableLayoutPanel2.PerformLayout();
|
||||
this.tableLayoutPanel4.ResumeLayout(false);
|
||||
this.tableLayoutPanel4.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -214,5 +262,8 @@
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
|
||||
private System.Windows.Forms.Button MyCancelButton;
|
||||
private System.Windows.Forms.Button OKButton;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
|
||||
private System.Windows.Forms.Label ProxyTypeLabel;
|
||||
private System.Windows.Forms.ComboBox ProxyTypeComboBox;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ namespace Shadowsocks.View
|
||||
private void UpdateTexts()
|
||||
{
|
||||
UseProxyCheckBox.Text = I18N.GetString("Use Proxy");
|
||||
ProxyTypeLabel.Text = I18N.GetString("Proxy Type");
|
||||
ProxyAddrLabel.Text = I18N.GetString("Proxy Addr");
|
||||
ProxyPortLable.Text = I18N.GetString("Proxy Port");
|
||||
OKButton.Text = I18N.GetString("OK");
|
||||
@@ -50,6 +51,7 @@ namespace Shadowsocks.View
|
||||
UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy;
|
||||
ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer;
|
||||
ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString();
|
||||
ProxyTypeComboBox.SelectedIndex = _modifiedConfiguration.proxyType;
|
||||
}
|
||||
|
||||
private void OKButton_Click(object sender, EventArgs e)
|
||||
@@ -58,12 +60,13 @@ namespace Shadowsocks.View
|
||||
{
|
||||
try
|
||||
{
|
||||
var type = ProxyTypeComboBox.SelectedIndex;
|
||||
var proxy = ProxyServerTextBox.Text;
|
||||
var port = int.Parse(ProxyPortTextBox.Text);
|
||||
Configuration.CheckServer(proxy);
|
||||
Configuration.CheckPort(port);
|
||||
|
||||
controller.EnableProxy(proxy, port);
|
||||
controller.EnableProxy(type, proxy, port);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
@@ -82,6 +85,7 @@ namespace Shadowsocks.View
|
||||
}
|
||||
|
||||
_modifiedConfiguration.useProxy = UseProxyCheckBox.Checked;
|
||||
_modifiedConfiguration.proxyType = ProxyTypeComboBox.SelectedIndex;
|
||||
_modifiedConfiguration.proxyServer = ProxyServerTextBox.Text;
|
||||
var tmpProxyPort = 0;
|
||||
int.TryParse(ProxyPortTextBox.Text, out tmpProxyPort);
|
||||
@@ -112,6 +116,7 @@ namespace Shadowsocks.View
|
||||
{
|
||||
ProxyServerTextBox.Enabled = true;
|
||||
ProxyPortTextBox.Enabled = true;
|
||||
ProxyTypeComboBox.Enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -119,6 +124,7 @@ namespace Shadowsocks.View
|
||||
ProxyPortTextBox.Clear();
|
||||
ProxyServerTextBox.Enabled = false;
|
||||
ProxyPortTextBox.Enabled = false;
|
||||
ProxyTypeComboBox.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
<Compile Include="Model\HotKeyConfig.cs" />
|
||||
<Compile Include="Model\ProxyConfig.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" />
|
||||
@@ -186,6 +187,7 @@
|
||||
<Compile Include="Util\Hotkeys.cs" />
|
||||
<Compile Include="Util\ProcessManagement\Job.cs" />
|
||||
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" />
|
||||
<Compile Include="Util\Sockets\LineReader.cs" />
|
||||
<Compile Include="Util\Sockets\SocketUtil.cs" />
|
||||
<Compile Include="Util\Sockets\WrappedSocket.cs" />
|
||||
<Compile Include="Util\Util.cs" />
|
||||
@@ -196,7 +198,7 @@
|
||||
<DependentUpon>ConfigForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controller\Service\TCPRelay.cs" />
|
||||
<Compile Include="Controller\Service\PolipoRunner.cs" />
|
||||
<Compile Include="Controller\Service\PrivoxyRunner.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Controller\ShadowsocksController.cs" />
|
||||
|
||||
Reference in New Issue
Block a user