Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b6d641d17 | ||
|
|
59c315d465 | ||
|
|
f8c9bdda12 | ||
|
|
707442163d | ||
|
|
bb7171674b | ||
|
|
3b1ac7500f | ||
|
|
2bebe515fa | ||
|
|
4367a56f46 | ||
|
|
d958c5c393 | ||
|
|
c3b7c3dcfd | ||
|
|
d73b77ff88 | ||
|
|
da234ef857 | ||
|
|
514bb02098 | ||
|
|
e364457b1b | ||
|
|
be85ab55a7 | ||
|
|
e6e9fc8588 | ||
|
|
4c5376ce0b | ||
|
|
0266ad9d59 | ||
|
|
cbc94e0eb7 | ||
|
|
f70b177e56 | ||
|
|
e7c00dd8e7 | ||
|
|
5b2327776a | ||
|
|
ed54b249db | ||
|
|
65d72fdfed | ||
|
|
a7b0ce5bcb | ||
|
|
f935514c05 | ||
|
|
37d894594e | ||
|
|
28fe2fdc54 | ||
|
|
f676d2185e | ||
|
|
b335e6c8f5 | ||
|
|
80fc8b4ccf | ||
|
|
70624e35d9 | ||
|
|
2ed8ab50cd | ||
|
|
a1eef6eac1 | ||
|
|
e30487207b | ||
|
|
8637277c27 | ||
|
|
2b4b6cba07 | ||
|
|
acf7963de7 | ||
|
|
64e21e031d | ||
|
|
ddb7374432 | ||
|
|
3417a984fa | ||
|
|
6cb58b229d | ||
|
|
53ce1948e4 | ||
|
|
ad15c7e64b | ||
|
|
aef88543d2 | ||
|
|
22740c5102 | ||
|
|
a02cd11dc9 | ||
|
|
21618ef08d | ||
|
|
e15e2cf700 |
@@ -1,10 +1,24 @@
|
||||
3.3 2016-09-09
|
||||
- Update Privoxy to 3.0.26
|
||||
- Change minimum system version explicitly to Windows Vista
|
||||
since we are using dual-mode socket
|
||||
- Support running multiple instances of Privoxy for system proxy
|
||||
- Improve networking
|
||||
- New traffic chart and icon style
|
||||
- I18N: Traditional Chinese support
|
||||
- Bug fixes and improvements
|
||||
|
||||
3.2 2016-08-13
|
||||
- Add AES-CTR, blowfish and camellia ciphers support, including aes-256-ctr, aes-192-ctr, aes-128-ctr, bf-cfb, camellia-128-cfb, camellia-192-cfb and camellia-256-cfb.
|
||||
- Add AES-CTR, blowfish and camellia ciphers support,
|
||||
including aes-256-ctr, aes-192-ctr, aes-128-ctr,
|
||||
bf-cfb, camellia-128-cfb, camellia-192-cfb
|
||||
and camellia-256-cfb.
|
||||
- Support one-time authentication in ss urls for sharing
|
||||
- Support traffic chart and traffic icon
|
||||
- Add proxy support
|
||||
- Add verbose logging
|
||||
- Improve LogForm
|
||||
- Delete log file when clicking "Clean Logs" in the LogForm
|
||||
- Bug fixes and improvements
|
||||
|
||||
3.1 2016-05-01
|
||||
|
||||
@@ -54,9 +54,6 @@ If you want to manage multiple servers using other tools like SwitchyOmega,
|
||||
you can start multiple Shadowsocks instances. To avoid configuration conflicts,
|
||||
copy Shadowsocks to a new directory and choose a different local port.
|
||||
|
||||
Also, make sure to use `SOCKS5` proxy in SwitchyOmega, since we have only
|
||||
one HTTP proxy instance.
|
||||
|
||||
#### Server Configuration
|
||||
|
||||
Please visit [Servers] for more information.
|
||||
@@ -75,8 +72,8 @@ Visual Studio 2015 is required.
|
||||
GPLv3
|
||||
|
||||
|
||||
[Appveyor]: https://ci.appveyor.com/project/icylogic/shadowsocks-windows-l9mwe
|
||||
[Build Status]: https://ci.appveyor.com/api/projects/status/ytllr9yjkbpc2tu2/branch/master
|
||||
[Appveyor]: https://ci.appveyor.com/project/wongsyrone/shadowsocks-windows-0cn3i
|
||||
[Build Status]: https://ci.appveyor.com/api/projects/status/47784ryn365vw56w/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
|
||||
|
||||
@@ -10,24 +10,37 @@ namespace Shadowsocks.Controller
|
||||
public class I18N
|
||||
{
|
||||
protected static Dictionary<string, string> Strings;
|
||||
|
||||
static void Init(string res)
|
||||
{
|
||||
using (var sr = new StringReader(res))
|
||||
{
|
||||
foreach (var line in sr.NonWhiteSpaceLines())
|
||||
{
|
||||
if (line[0] == '#')
|
||||
continue;
|
||||
|
||||
var pos = line.IndexOf('=');
|
||||
if (pos < 1)
|
||||
continue;
|
||||
Strings[line.Substring(0, pos)] = line.Substring(pos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static I18N()
|
||||
{
|
||||
Strings = new Dictionary<string, string>();
|
||||
|
||||
if (CultureInfo.CurrentCulture.IetfLanguageTag.StartsWith("zh", StringComparison.OrdinalIgnoreCase))
|
||||
string name = CultureInfo.CurrentCulture.Name;
|
||||
if (name.StartsWith("zh"))
|
||||
{
|
||||
using (var sr = new StringReader(Resources.cn))
|
||||
if (name == "zh" || name == "zh-CN")
|
||||
{
|
||||
foreach (var line in sr.NonWhiteSpaceLines())
|
||||
{
|
||||
if (line[0] == '#')
|
||||
continue;
|
||||
|
||||
var pos = line.IndexOf('=');
|
||||
if (pos < 1)
|
||||
continue;
|
||||
Strings[line.Substring(0, pos)] = line.Substring(pos + 1);
|
||||
}
|
||||
Init(Resources.cn);
|
||||
}
|
||||
else
|
||||
{
|
||||
Init(Resources.zh_tw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
public static string LogFilePath;
|
||||
|
||||
private static FileStream fs;
|
||||
private static StreamWriterWithTimestamp sw;
|
||||
private static FileStream _fs;
|
||||
private static StreamWriterWithTimestamp _sw;
|
||||
|
||||
public static bool OpenLogFile()
|
||||
{
|
||||
@@ -21,11 +21,11 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
LogFilePath = Utils.GetTempPath("shadowsocks.log");
|
||||
|
||||
fs = new FileStream(LogFilePath, FileMode.Append);
|
||||
sw = new StreamWriterWithTimestamp(fs);
|
||||
sw.AutoFlush = true;
|
||||
Console.SetOut(sw);
|
||||
Console.SetError(sw);
|
||||
_fs = new FileStream(LogFilePath, FileMode.Append);
|
||||
_sw = new StreamWriterWithTimestamp(_fs);
|
||||
_sw.AutoFlush = true;
|
||||
Console.SetOut(_sw);
|
||||
Console.SetError(_sw);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -54,11 +54,11 @@ namespace Shadowsocks.Controller
|
||||
WriteToLogFile(o);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
sw.Close();
|
||||
sw.Dispose();
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
public static void Clear() {
|
||||
_sw.Close();
|
||||
_sw.Dispose();
|
||||
_fs.Close();
|
||||
_fs.Dispose();
|
||||
File.Delete(LogFilePath);
|
||||
OpenLogFile();
|
||||
}
|
||||
|
||||
@@ -34,12 +34,39 @@ namespace Shadowsocks.Controller
|
||||
//records cache for current server in {_monitorInterval} minutes
|
||||
private readonly ConcurrentDictionary<string, List<int>> _latencyRecords = new ConcurrentDictionary<string, List<int>>();
|
||||
//speed in KiB/s
|
||||
private readonly ConcurrentDictionary<string, long> _inboundCounter = new ConcurrentDictionary<string, long>();
|
||||
private readonly ConcurrentDictionary<string, long> _lastInboundCounter = new ConcurrentDictionary<string, long>();
|
||||
private readonly ConcurrentDictionary<string, List<int>> _inboundSpeedRecords = new ConcurrentDictionary<string, List<int>>();
|
||||
private readonly ConcurrentDictionary<string, long> _outboundCounter = new ConcurrentDictionary<string, long>();
|
||||
private readonly ConcurrentDictionary<string, long> _lastOutboundCounter = new ConcurrentDictionary<string, long>();
|
||||
private readonly ConcurrentDictionary<string, List<int>> _outboundSpeedRecords = new ConcurrentDictionary<string, List<int>>();
|
||||
private readonly ConcurrentDictionary<string, InOutBoundRecord> _inOutBoundRecords = new ConcurrentDictionary<string, InOutBoundRecord>();
|
||||
|
||||
private class InOutBoundRecord
|
||||
{
|
||||
private long _inbound;
|
||||
private long _lastInbound;
|
||||
private long _outbound;
|
||||
private long _lastOutbound;
|
||||
|
||||
public void UpdateInbound(long delta)
|
||||
{
|
||||
Interlocked.Add(ref _inbound, delta);
|
||||
}
|
||||
|
||||
public void UpdateOutbound(long delta)
|
||||
{
|
||||
Interlocked.Add(ref _outbound, delta);
|
||||
}
|
||||
|
||||
public void GetDelta(out long inboundDelta, out long outboundDelta)
|
||||
{
|
||||
var i = Interlocked.Read(ref _inbound);
|
||||
var il = Interlocked.Exchange(ref _lastInbound, i);
|
||||
inboundDelta = i - il;
|
||||
|
||||
|
||||
var o = Interlocked.Read(ref _outbound);
|
||||
var ol = Interlocked.Exchange(ref _lastOutbound, o);
|
||||
outboundDelta = o - ol;
|
||||
}
|
||||
}
|
||||
|
||||
//tasks
|
||||
private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1);
|
||||
@@ -98,36 +125,26 @@ namespace Shadowsocks.Controller
|
||||
|
||||
private void UpdateSpeed(object _)
|
||||
{
|
||||
foreach (var kv in _lastInboundCounter)
|
||||
foreach (var kv in _inOutBoundRecords)
|
||||
{
|
||||
var id = kv.Key;
|
||||
var record = kv.Value;
|
||||
|
||||
var lastInbound = kv.Value;
|
||||
var inbound = _inboundCounter[id];
|
||||
var bytes = inbound - lastInbound;
|
||||
_lastInboundCounter[id] = inbound;
|
||||
var inboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds);
|
||||
_inboundSpeedRecords.GetOrAdd(id, (k) =>
|
||||
{
|
||||
List<int> records = new List<int>();
|
||||
records.Add(inboundSpeed);
|
||||
return records;
|
||||
});
|
||||
long inboundDelta, outboundDelta;
|
||||
|
||||
var lastOutbound = _lastOutboundCounter[id];
|
||||
var outbound = _outboundCounter[id];
|
||||
bytes = outbound - lastOutbound;
|
||||
_lastOutboundCounter[id] = outbound;
|
||||
var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds);
|
||||
_outboundSpeedRecords.GetOrAdd(id, (k) =>
|
||||
{
|
||||
List<int> records = new List<int>();
|
||||
records.Add(outboundSpeed);
|
||||
return records;
|
||||
});
|
||||
record.GetDelta(out inboundDelta, out outboundDelta);
|
||||
|
||||
var inboundSpeed = GetSpeedInKiBPerSecond(inboundDelta, _monitorInterval.TotalSeconds);
|
||||
var outboundSpeed = GetSpeedInKiBPerSecond(outboundDelta, _monitorInterval.TotalSeconds);
|
||||
|
||||
var inR = _inboundSpeedRecords.GetOrAdd(id, (k) => new List<int>());
|
||||
var outR = _outboundSpeedRecords.GetOrAdd(id, (k) => new List<int>());
|
||||
|
||||
inR.Add(inboundSpeed);
|
||||
outR.Add(outboundSpeed);
|
||||
|
||||
Logging.Debug(
|
||||
$"{id}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords[id].Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords[id].Max()} KiB/s");
|
||||
$"{id}: current/max inbound {inboundSpeed}/{inR.Max()} KiB/s, current/max outbound {outboundSpeed}/{outR.Max()} KiB/s");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,20 +344,32 @@ namespace Shadowsocks.Controller
|
||||
|
||||
public void UpdateInboundCounter(Server server, long n)
|
||||
{
|
||||
_inboundCounter.AddOrUpdate(server.Identifier(), (k) =>
|
||||
_inOutBoundRecords.AddOrUpdate(server.Identifier(), (k) =>
|
||||
{
|
||||
_lastInboundCounter.GetOrAdd(server.Identifier(), 0);
|
||||
return n;
|
||||
}, (k, v) => (v + n));
|
||||
var r = new InOutBoundRecord();
|
||||
r.UpdateInbound(n);
|
||||
|
||||
return r;
|
||||
}, (k, v) =>
|
||||
{
|
||||
v.UpdateInbound(n);
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateOutboundCounter(Server server, long n)
|
||||
{
|
||||
_outboundCounter.AddOrUpdate(server.Identifier(), (k) =>
|
||||
_inOutBoundRecords.AddOrUpdate(server.Identifier(), (k) =>
|
||||
{
|
||||
_lastOutboundCounter.GetOrAdd(server.Identifier(), 0);
|
||||
return n;
|
||||
}, (k, v) => (v + n));
|
||||
var r = new InOutBoundRecord();
|
||||
r.UpdateOutbound(n);
|
||||
|
||||
return r;
|
||||
}, (k, v) =>
|
||||
{
|
||||
v.UpdateOutbound(n);
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
class UpdateRecordsState
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
@@ -10,13 +11,23 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
public class Listener
|
||||
{
|
||||
public interface Service
|
||||
public interface IService
|
||||
{
|
||||
bool Handle(byte[] firstPacket, int length, Socket socket, object state);
|
||||
|
||||
void Stop();
|
||||
}
|
||||
|
||||
public abstract class Service : IService
|
||||
{
|
||||
public abstract bool Handle(byte[] firstPacket, int length, Socket socket, object state);
|
||||
|
||||
public virtual void Stop() { }
|
||||
}
|
||||
|
||||
public class UDPState
|
||||
{
|
||||
public Socket socket;
|
||||
public byte[] buffer = new byte[4096];
|
||||
public EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
}
|
||||
@@ -25,9 +36,9 @@ namespace Shadowsocks.Controller
|
||||
bool _shareOverLAN;
|
||||
Socket _tcpSocket;
|
||||
Socket _udpSocket;
|
||||
IList<Service> _services;
|
||||
List<IService> _services;
|
||||
|
||||
public Listener(IList<Service> services)
|
||||
public Listener(List<IService> services)
|
||||
{
|
||||
this._services = services;
|
||||
}
|
||||
@@ -76,6 +87,7 @@ namespace Shadowsocks.Controller
|
||||
Logging.Info("Shadowsocks started");
|
||||
_tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket);
|
||||
UDPState udpState = new UDPState();
|
||||
udpState.socket = _udpSocket;
|
||||
_udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState);
|
||||
}
|
||||
catch (SocketException)
|
||||
@@ -97,17 +109,20 @@ namespace Shadowsocks.Controller
|
||||
_udpSocket.Close();
|
||||
_udpSocket = null;
|
||||
}
|
||||
|
||||
_services.ForEach(s=>s.Stop());
|
||||
}
|
||||
|
||||
public void RecvFromCallback(IAsyncResult ar)
|
||||
{
|
||||
UDPState state = (UDPState)ar.AsyncState;
|
||||
var socket = state.socket;
|
||||
try
|
||||
{
|
||||
int bytesRead = _udpSocket.EndReceiveFrom(ar, ref state.remoteEndPoint);
|
||||
foreach (Service service in _services)
|
||||
int bytesRead = socket.EndReceiveFrom(ar, ref state.remoteEndPoint);
|
||||
foreach (IService service in _services)
|
||||
{
|
||||
if (service.Handle(state.buffer, bytesRead, _udpSocket, state))
|
||||
if (service.Handle(state.buffer, bytesRead, socket, state))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -116,14 +131,15 @@ namespace Shadowsocks.Controller
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Debug(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
_udpSocket.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, 0, ref state.remoteEndPoint, new AsyncCallback(RecvFromCallback), state);
|
||||
socket.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, 0, ref state.remoteEndPoint, new AsyncCallback(RecvFromCallback), state);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
@@ -186,7 +202,7 @@ namespace Shadowsocks.Controller
|
||||
try
|
||||
{
|
||||
int bytesRead = conn.EndReceive(ar);
|
||||
foreach (Service service in _services)
|
||||
foreach (IService service in _services)
|
||||
{
|
||||
if (service.Handle(buf, bytesRead, conn, null))
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
@@ -35,7 +36,7 @@ namespace Shadowsocks.Controller
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
{
|
||||
if (socket.ProtocolType != ProtocolType.Tcp)
|
||||
{
|
||||
@@ -69,7 +70,7 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
else if (kv.Length == 1)
|
||||
{
|
||||
if (line.IndexOf("pac") >= 0)
|
||||
if (line.IndexOf("pac", StringComparison.Ordinal) >= 0)
|
||||
{
|
||||
pathMatch = true;
|
||||
}
|
||||
@@ -207,7 +208,7 @@ Connection: Close
|
||||
private void PACFileWatcher_Changed(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
string path = e.FullPath.ToString();
|
||||
string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString();
|
||||
string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
// if there is no path info stored yet or stored path has different time of write then the one now is inspected
|
||||
if (!fileChangedTime.ContainsKey(path) || fileChangedTime[path].ToString() != currentLastWriteTime)
|
||||
@@ -226,7 +227,7 @@ Connection: Close
|
||||
private void UserRuleFileWatcher_Changed(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
string path = e.FullPath.ToString();
|
||||
string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString();
|
||||
string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
// if there is no path info stored yet or stored path has different time of write then the one now is inspected
|
||||
if (!fileChangedTime.ContainsKey(path) || fileChangedTime[path].ToString() != currentLastWriteTime)
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using System.Windows.Forms;
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
using Shadowsocks.Util.ProcessManagement;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
class PolipoRunner
|
||||
{
|
||||
private static int Uid;
|
||||
private static string UniqueConfigFile;
|
||||
private static Job PolipoJob;
|
||||
private Process _process;
|
||||
private int _runningPort;
|
||||
|
||||
@@ -22,6 +28,10 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
Uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different polipo instance.
|
||||
UniqueConfigFile = $"privoxy_{Uid}.conf";
|
||||
PolipoJob = new Job();
|
||||
|
||||
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe);
|
||||
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll);
|
||||
}
|
||||
@@ -45,7 +55,7 @@ namespace Shadowsocks.Controller
|
||||
if (_process == null)
|
||||
{
|
||||
Process[] existingPolipo = Process.GetProcessesByName("ss_privoxy");
|
||||
foreach (Process p in existingPolipo)
|
||||
foreach (Process p in existingPolipo.Where(IsChildProcess))
|
||||
{
|
||||
KillProcess(p);
|
||||
}
|
||||
@@ -54,17 +64,23 @@ namespace Shadowsocks.Controller
|
||||
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("privoxy.conf"), Encoding.UTF8.GetBytes(polipoConfig));
|
||||
FileManager.ByteArrayToFile(Utils.GetTempPath(UniqueConfigFile), Encoding.UTF8.GetBytes(polipoConfig));
|
||||
|
||||
_process = new Process();
|
||||
// Configure the process using the StartInfo properties.
|
||||
_process.StartInfo.FileName = "ss_privoxy.exe";
|
||||
_process.StartInfo.Arguments = "privoxy.conf";
|
||||
_process.StartInfo.Arguments = UniqueConfigFile;
|
||||
_process.StartInfo.WorkingDirectory = Utils.GetTempPath();
|
||||
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
_process.StartInfo.UseShellExecute = true;
|
||||
_process.StartInfo.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.
|
||||
*/
|
||||
PolipoJob.AddProcess(_process.Handle);
|
||||
}
|
||||
RefreshTrayArea();
|
||||
}
|
||||
@@ -97,6 +113,59 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
if (Utils.IsPortableMode())
|
||||
{
|
||||
/*
|
||||
* Under PortableMode, we could identify it by the path of ss_privoxy.exe.
|
||||
*/
|
||||
try
|
||||
{
|
||||
/*
|
||||
* Sometimes Process.GetProcessesByName will return some processes that
|
||||
* are already dead, and that will cause exceptions here.
|
||||
* We could simply ignore those exceptions.
|
||||
*/
|
||||
string path = process.MainModule.FileName;
|
||||
return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.LogUsefulException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var cmd = process.GetCommandLine();
|
||||
|
||||
return cmd.Contains(UniqueConfigFile);
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
if ((uint) ex.ErrorCode != 0x80004005)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetFreePort()
|
||||
{
|
||||
int defaultPort = 8123;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Shadowsocks.Controller
|
||||
this._targetPort = targetPort;
|
||||
}
|
||||
|
||||
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
{
|
||||
if (socket.ProtocolType != ProtocolType.Tcp)
|
||||
{
|
||||
@@ -45,19 +46,10 @@ namespace Shadowsocks.Controller
|
||||
this._local = socket;
|
||||
try
|
||||
{
|
||||
// TODO async resolving
|
||||
IPAddress ipAddress;
|
||||
bool parsed = IPAddress.TryParse("127.0.0.1", out ipAddress);
|
||||
IPEndPoint remoteEP = new IPEndPoint(ipAddress, targetPort);
|
||||
|
||||
|
||||
_remote = new Socket(ipAddress.AddressFamily,
|
||||
SocketType.Stream, ProtocolType.Tcp);
|
||||
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
||||
EndPoint remoteEP = SocketUtil.GetEndPoint("localhost", targetPort);
|
||||
|
||||
// Connect to the remote endpoint.
|
||||
_remote.BeginConnect(remoteEP,
|
||||
new AsyncCallback(ConnectCallback), null);
|
||||
SocketUtil.BeginConnectTcp(remoteEP, ConnectCallback, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -74,7 +66,7 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
try
|
||||
{
|
||||
_remote.EndConnect(ar);
|
||||
_remote = SocketUtil.EndConnectTcp(ar);
|
||||
HandshakeReceive();
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -9,6 +9,7 @@ using Shadowsocks.Controller.Strategy;
|
||||
using Shadowsocks.Encryption;
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Proxy;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
@@ -28,7 +29,7 @@ namespace Shadowsocks.Controller
|
||||
_lastSweepTime = DateTime.Now;
|
||||
}
|
||||
|
||||
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
{
|
||||
if (socket.ProtocolType != ProtocolType.Tcp
|
||||
|| (length < 2 || firstPacket[0] != 5))
|
||||
@@ -61,6 +62,16 @@ namespace Shadowsocks.Controller
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
List<TCPHandler> handlersToClose = new List<TCPHandler>();
|
||||
lock (Handlers)
|
||||
{
|
||||
handlersToClose.AddRange(Handlers);
|
||||
}
|
||||
handlersToClose.ForEach(h=>h.Close());
|
||||
}
|
||||
|
||||
public void UpdateInboundCounter(Server server, long n)
|
||||
{
|
||||
_controller.UpdateInboundCounter(server, n);
|
||||
@@ -79,6 +90,33 @@ namespace Shadowsocks.Controller
|
||||
|
||||
class TCPHandler
|
||||
{
|
||||
|
||||
class AsyncSession
|
||||
{
|
||||
public IProxy Remote { get; }
|
||||
|
||||
public AsyncSession(IProxy remote)
|
||||
{
|
||||
Remote = remote;
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncSession<T> : AsyncSession
|
||||
{
|
||||
public T State { get; set; }
|
||||
|
||||
public AsyncSession(IProxy remote, T state) : base(remote)
|
||||
{
|
||||
State = state;
|
||||
}
|
||||
|
||||
public AsyncSession(AsyncSession session, T state): base(session.Remote)
|
||||
{
|
||||
State = state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Size of receive buffer.
|
||||
public static readonly int RecvSize = 8192;
|
||||
public static readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth
|
||||
@@ -88,7 +126,8 @@ namespace Shadowsocks.Controller
|
||||
public IEncryptor encryptor;
|
||||
public Server server;
|
||||
// Client socket.
|
||||
public IProxy remote;
|
||||
private AsyncSession _currentRemoteSession;
|
||||
|
||||
public Socket connection;
|
||||
public ShadowsocksController controller;
|
||||
public TCPRelay tcprelay;
|
||||
@@ -137,7 +176,13 @@ namespace Shadowsocks.Controller
|
||||
Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint);
|
||||
if (server == null || server.server == "")
|
||||
throw new ArgumentException("No server configured");
|
||||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
|
||||
lock (_encryptionLock)
|
||||
{
|
||||
lock (_decryptionLock)
|
||||
{
|
||||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
|
||||
}
|
||||
}
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@@ -176,6 +221,7 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
try
|
||||
{
|
||||
var remote = _currentRemoteSession?.Remote;
|
||||
remote?.Shutdown(SocketShutdown.Both);
|
||||
remote?.Close();
|
||||
}
|
||||
@@ -302,7 +348,6 @@ namespace Shadowsocks.Controller
|
||||
if (ar.AsyncState != null)
|
||||
{
|
||||
connection.EndSend(ar);
|
||||
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay");
|
||||
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
|
||||
}
|
||||
else
|
||||
@@ -310,7 +355,6 @@ namespace Shadowsocks.Controller
|
||||
int bytesRead = connection.EndReceive(ar);
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay");
|
||||
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
|
||||
}
|
||||
else
|
||||
@@ -341,6 +385,8 @@ namespace Shadowsocks.Controller
|
||||
// inner class
|
||||
private class ProxyTimer : Timer
|
||||
{
|
||||
public AsyncSession Session;
|
||||
|
||||
public EndPoint DestEndPoint;
|
||||
public Server Server;
|
||||
|
||||
@@ -351,6 +397,8 @@ namespace Shadowsocks.Controller
|
||||
|
||||
private class ServerTimer : Timer
|
||||
{
|
||||
public AsyncSession Session;
|
||||
|
||||
public Server Server;
|
||||
public ServerTimer(int p) : base(p) { }
|
||||
}
|
||||
@@ -361,48 +409,36 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
CreateRemote();
|
||||
|
||||
// TODO async resolving
|
||||
IPAddress ipAddress;
|
||||
bool parsed = IPAddress.TryParse(server.server, out ipAddress);
|
||||
if (!parsed)
|
||||
{
|
||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server);
|
||||
ipAddress = ipHostInfo.AddressList[0];
|
||||
}
|
||||
IPEndPoint destEP = new IPEndPoint(ipAddress, server.server_port);
|
||||
|
||||
// Setting up proxy
|
||||
IPEndPoint proxyEP;
|
||||
IProxy remote;
|
||||
EndPoint proxyEP;
|
||||
if (_config.useProxy)
|
||||
{
|
||||
parsed = IPAddress.TryParse(_config.proxyServer, out ipAddress);
|
||||
if (!parsed)
|
||||
{
|
||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(_config.proxyServer);
|
||||
ipAddress = ipHostInfo.AddressList[0];
|
||||
}
|
||||
|
||||
remote = new Socks5Proxy();
|
||||
proxyEP = new IPEndPoint(ipAddress, _config.proxyPort);
|
||||
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
remote = new DirectConnect();
|
||||
proxyEP = destEP;
|
||||
proxyEP = null;
|
||||
}
|
||||
|
||||
var session = new AsyncSession(remote);
|
||||
_currentRemoteSession = session;
|
||||
|
||||
ProxyTimer proxyTimer = new ProxyTimer(3000);
|
||||
proxyTimer.AutoReset = false;
|
||||
proxyTimer.Elapsed += proxyConnectTimer_Elapsed;
|
||||
proxyTimer.Enabled = true;
|
||||
proxyTimer.DestEndPoint = destEP;
|
||||
|
||||
proxyTimer.Session = session;
|
||||
proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port);
|
||||
proxyTimer.Server = server;
|
||||
|
||||
_proxyConnected = false;
|
||||
|
||||
// Connect to the proxy server.
|
||||
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), proxyTimer);
|
||||
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), new AsyncSession<ProxyTimer>(remote, proxyTimer));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -413,14 +449,20 @@ namespace Shadowsocks.Controller
|
||||
|
||||
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var timer = (ProxyTimer) sender;
|
||||
timer.Elapsed -= proxyConnectTimer_Elapsed;
|
||||
timer.Enabled = false;
|
||||
timer.Dispose();
|
||||
|
||||
|
||||
if (_proxyConnected || _destConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var ep = ((ProxyTimer)sender).DestEndPoint;
|
||||
var proxy = timer.Session.Remote;
|
||||
|
||||
Logging.Info($"Proxy {ep} timed out");
|
||||
remote?.Close();
|
||||
Logging.Info($"Proxy {proxy.ProxyEndPoint} timed out");
|
||||
proxy.Close();
|
||||
RetryConnect();
|
||||
}
|
||||
|
||||
@@ -433,13 +475,16 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
try
|
||||
{
|
||||
ProxyTimer timer = (ProxyTimer)ar.AsyncState;
|
||||
var destEP = timer.DestEndPoint;
|
||||
var session = (AsyncSession<ProxyTimer>) ar.AsyncState;
|
||||
ProxyTimer timer = session.State;
|
||||
var destEndPoint = timer.DestEndPoint;
|
||||
server = timer.Server;
|
||||
timer.Elapsed -= proxyConnectTimer_Elapsed;
|
||||
timer.Enabled = false;
|
||||
timer.Dispose();
|
||||
|
||||
var remote = session.Remote;
|
||||
|
||||
// Complete the connection.
|
||||
remote.EndConnectProxy(ar);
|
||||
|
||||
@@ -458,11 +503,12 @@ namespace Shadowsocks.Controller
|
||||
connectTimer.AutoReset = false;
|
||||
connectTimer.Elapsed += destConnectTimer_Elapsed;
|
||||
connectTimer.Enabled = true;
|
||||
connectTimer.Session = session;
|
||||
connectTimer.Server = server;
|
||||
|
||||
_destConnected = false;
|
||||
// Connect to the remote endpoint.
|
||||
remote.BeginConnectDest(destEP, new AsyncCallback(ConnectCallback), connectTimer);
|
||||
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), new AsyncSession<ServerTimer>(session, connectTimer));
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
@@ -476,16 +522,22 @@ namespace Shadowsocks.Controller
|
||||
|
||||
private void destConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var timer = (ServerTimer)sender;
|
||||
timer.Elapsed -= destConnectTimer_Elapsed;
|
||||
timer.Enabled = false;
|
||||
timer.Dispose();
|
||||
|
||||
if (_destConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Server server = ((ServerTimer)sender).Server;
|
||||
var session = timer.Session;
|
||||
Server server = timer.Server;
|
||||
IStrategy strategy = controller.GetCurrentStrategy();
|
||||
strategy?.SetFailure(server);
|
||||
Logging.Info($"{server.FriendlyName()} timed out");
|
||||
remote?.Close();
|
||||
session.Remote.Close();
|
||||
RetryConnect();
|
||||
}
|
||||
|
||||
@@ -506,12 +558,14 @@ namespace Shadowsocks.Controller
|
||||
if (_closed) return;
|
||||
try
|
||||
{
|
||||
ServerTimer timer = (ServerTimer)ar.AsyncState;
|
||||
var session = (AsyncSession<ServerTimer>) ar.AsyncState;
|
||||
ServerTimer timer = session.State;
|
||||
server = timer.Server;
|
||||
timer.Elapsed -= destConnectTimer_Elapsed;
|
||||
timer.Enabled = false;
|
||||
timer.Dispose();
|
||||
|
||||
var remote = session.Remote;
|
||||
// Complete the connection.
|
||||
remote?.EndConnectDest(ar);
|
||||
|
||||
@@ -527,7 +581,7 @@ namespace Shadowsocks.Controller
|
||||
strategy?.UpdateLatency(server, latency);
|
||||
_tcprelay.UpdateLatency(server, latency);
|
||||
|
||||
StartPipe();
|
||||
StartPipe(session);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
@@ -544,14 +598,15 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
}
|
||||
|
||||
private void StartPipe()
|
||||
private void StartPipe(AsyncSession session)
|
||||
{
|
||||
if (_closed) return;
|
||||
try
|
||||
{
|
||||
_startReceivingTime = DateTime.Now;
|
||||
remote?.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), null);
|
||||
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), null);
|
||||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
|
||||
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
|
||||
new AsyncSession<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -565,8 +620,8 @@ namespace Shadowsocks.Controller
|
||||
if (_closed) return;
|
||||
try
|
||||
{
|
||||
if ( remote == null ) return;
|
||||
int bytesRead = remote.EndReceive(ar);
|
||||
var session = (AsyncSession) ar.AsyncState;
|
||||
int bytesRead = session.Remote.EndReceive(ar);
|
||||
_totalRead += bytesRead;
|
||||
_tcprelay.UpdateInboundCounter(server, bytesRead);
|
||||
if (bytesRead > 0)
|
||||
@@ -578,7 +633,7 @@ namespace Shadowsocks.Controller
|
||||
if (_closed) return;
|
||||
encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
|
||||
}
|
||||
connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), null);
|
||||
connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
|
||||
IStrategy strategy = controller.GetCurrentStrategy();
|
||||
strategy?.UpdateLastRead(server);
|
||||
}
|
||||
@@ -604,36 +659,48 @@ namespace Shadowsocks.Controller
|
||||
if(connection == null) return;
|
||||
int bytesRead = connection.EndReceive(ar);
|
||||
_totalWrite += bytesRead;
|
||||
|
||||
var session = (AsyncSession<bool>) ar.AsyncState;
|
||||
var remote = session.Remote;
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
int atyp = _connetionRecvBuffer[0];
|
||||
string dst_addr;
|
||||
int dst_port;
|
||||
switch (atyp)
|
||||
/*
|
||||
* Only the first packet contains the socks5 header, it doesn't make sense to parse every packets.
|
||||
* Also it's unnecessary to parse these data if we turn off the VerboseLogging.
|
||||
*/
|
||||
if (session.State && _config.isVerboseLogging)
|
||||
{
|
||||
case 1: // IPv4 address, 4 bytes
|
||||
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
|
||||
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6];
|
||||
if ( _config.isVerboseLogging ) {
|
||||
Logging.Info( $"connect to {dst_addr}:{dst_port}" );
|
||||
}
|
||||
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];
|
||||
if ( _config.isVerboseLogging ) {
|
||||
Logging.Info( $"connect to {dst_addr}:{dst_port}" );
|
||||
}
|
||||
break;
|
||||
case 4: // IPv6 address, 16 bytes
|
||||
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray()).ToString();
|
||||
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
|
||||
if ( _config.isVerboseLogging ) {
|
||||
Logging.Info( $"connect to [{dst_addr}]:{dst_port}" );
|
||||
}
|
||||
break;
|
||||
int atyp = _connetionRecvBuffer[0];
|
||||
string dst_addr;
|
||||
int dst_port;
|
||||
switch (atyp)
|
||||
{
|
||||
case 1: // IPv4 address, 4 bytes
|
||||
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
|
||||
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6];
|
||||
|
||||
Logging.Info($"connect to {dst_addr}:{dst_port}");
|
||||
session.State = false;
|
||||
break;
|
||||
case 3: // domain name, length + str
|
||||
int len = _connetionRecvBuffer[1];
|
||||
dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len);
|
||||
dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3];
|
||||
|
||||
Logging.Info($"connect to {dst_addr}:{dst_port}");
|
||||
session.State = false;
|
||||
break;
|
||||
case 4: // IPv6 address, 16 bytes
|
||||
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray()).ToString();
|
||||
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
|
||||
|
||||
Logging.Info($"connect to [{dst_addr}]:{dst_port}");
|
||||
session.State = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int bytesToSend;
|
||||
lock (_encryptionLock)
|
||||
{
|
||||
@@ -643,7 +710,7 @@ namespace Shadowsocks.Controller
|
||||
_tcprelay.UpdateOutboundCounter(server, bytesToSend);
|
||||
_startSendingTime = DateTime.Now;
|
||||
_bytesToSend = bytesToSend;
|
||||
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), null);
|
||||
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
|
||||
IStrategy strategy = controller.GetCurrentStrategy();
|
||||
strategy?.UpdateLastWrite(server);
|
||||
}
|
||||
@@ -666,8 +733,9 @@ namespace Shadowsocks.Controller
|
||||
if (_closed) return;
|
||||
try
|
||||
{
|
||||
remote?.EndSend(ar);
|
||||
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), null);
|
||||
var session = (AsyncSession)ar.AsyncState;
|
||||
session.Remote.EndSend(ar);
|
||||
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -681,8 +749,9 @@ namespace Shadowsocks.Controller
|
||||
if (_closed) return;
|
||||
try
|
||||
{
|
||||
var session = (AsyncSession)ar.AsyncState;
|
||||
connection?.EndSend(ar);
|
||||
remote?.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), null);
|
||||
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Shadowsocks.Controller
|
||||
this._cache = new LRUCache<IPEndPoint, UDPHandler>(512); // todo: choose a smart number
|
||||
}
|
||||
|
||||
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
||||
{
|
||||
if (socket.ProtocolType != ProtocolType.Udp)
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Shadowsocks.Controller
|
||||
public string LatestVersionLocalName;
|
||||
public event EventHandler CheckUpdateCompleted;
|
||||
|
||||
public const string Version = "3.2";
|
||||
public const string Version = "3.3";
|
||||
|
||||
private class CheckUpdateTimer : System.Timers.Timer
|
||||
{
|
||||
@@ -198,7 +198,7 @@ namespace Shadowsocks.Controller
|
||||
name = (string)asset["name"];
|
||||
browser_download_url = (string)asset["browser_download_url"];
|
||||
version = ParseVersionFromURL(browser_download_url);
|
||||
prerelease = browser_download_url.IndexOf("prerelease") >= 0;
|
||||
prerelease = browser_download_url.IndexOf("prerelease", StringComparison.Ordinal) >= 0;
|
||||
}
|
||||
|
||||
private static string ParseVersionFromURL(string url)
|
||||
|
||||
@@ -33,8 +33,10 @@ namespace Shadowsocks.Controller
|
||||
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance;
|
||||
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }
|
||||
|
||||
public long inboundCounter = 0;
|
||||
public long outboundCounter = 0;
|
||||
private long _inboundCounter = 0;
|
||||
private long _outboundCounter = 0;
|
||||
public long InboundCounter => Interlocked.Read(ref _inboundCounter);
|
||||
public long OutboundCounter => Interlocked.Read(ref _outboundCounter);
|
||||
public QueueLast<TrafficPerSecond> traffic;
|
||||
|
||||
private bool stopped = false;
|
||||
@@ -87,7 +89,7 @@ namespace Shadowsocks.Controller
|
||||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
|
||||
_strategyManager = new StrategyManager(this);
|
||||
StartReleasingMemory();
|
||||
StartTrafficStatistics(60);
|
||||
StartTrafficStatistics(61);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
@@ -364,7 +366,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
public void UpdateInboundCounter(Server server, long n)
|
||||
{
|
||||
Interlocked.Add(ref inboundCounter, n);
|
||||
Interlocked.Add(ref _inboundCounter, n);
|
||||
if (_config.availabilityStatistics)
|
||||
{
|
||||
availabilityStatistics.UpdateInboundCounter(server, n);
|
||||
@@ -373,7 +375,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
public void UpdateOutboundCounter(Server server, long n)
|
||||
{
|
||||
Interlocked.Add(ref outboundCounter, n);
|
||||
Interlocked.Add(ref _outboundCounter, n);
|
||||
if (_config.availabilityStatistics)
|
||||
{
|
||||
availabilityStatistics.UpdateOutboundCounter(server, n);
|
||||
@@ -427,7 +429,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
TCPRelay tcpRelay = new TCPRelay(this, _config);
|
||||
UDPRelay udpRelay = new UDPRelay(this);
|
||||
List<Listener.Service> services = new List<Listener.Service>();
|
||||
List<Listener.IService> services = new List<Listener.IService>();
|
||||
services.Add(tcpRelay);
|
||||
services.Add(udpRelay);
|
||||
services.Add(_pacServer);
|
||||
@@ -579,19 +581,17 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
TrafficPerSecond previous = traffic.Last;
|
||||
TrafficPerSecond current = new TrafficPerSecond();
|
||||
current.inboundCounter = inboundCounter;
|
||||
current.outboundCounter = outboundCounter;
|
||||
current.inboundIncreasement = inboundCounter - previous.inboundCounter;
|
||||
current.outboundIncreasement = outboundCounter - previous.outboundCounter;
|
||||
|
||||
var inbound = current.inboundCounter = InboundCounter;
|
||||
var outbound = current.outboundCounter = OutboundCounter;
|
||||
current.inboundIncreasement = inbound - previous.inboundCounter;
|
||||
current.outboundIncreasement = outbound - previous.outboundCounter;
|
||||
|
||||
traffic.Enqueue(current);
|
||||
if (traffic.Count > queueMaxSize)
|
||||
traffic.Dequeue();
|
||||
|
||||
if (TrafficChanged != null)
|
||||
{
|
||||
TrafficChanged(this, new EventArgs());
|
||||
}
|
||||
TrafficChanged?.Invoke(this, new EventArgs());
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ Password=密码
|
||||
Encryption=加密
|
||||
Proxy Port=代理端口
|
||||
Remarks=备注
|
||||
Onetime Authentication (Experimental)=一次性认证(实验性)
|
||||
Onetime Authentication=一次性认证
|
||||
OK=确定
|
||||
Cancel=取消
|
||||
New server=未配置的服务器
|
||||
@@ -113,4 +113,6 @@ 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。
|
||||
Proxy request failed=代理请求失败
|
||||
Proxy handshake failed=代理握手失败
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,118 @@
|
||||
# translation for Traditional Chinese
|
||||
|
||||
Shadowsocks=Shadowsocks
|
||||
|
||||
# Menu items
|
||||
|
||||
Enable System Proxy=啟用系統代理
|
||||
Mode=系統代理模式
|
||||
PAC=PAC 模式
|
||||
Global=全局模式
|
||||
Servers=伺服器
|
||||
Edit Servers...=編輯伺服器...
|
||||
Statistics Config...=統計配置...
|
||||
Start on Boot=開機啟動
|
||||
SOCKS5 Proxy...=SOCKS5代理設置...
|
||||
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...=掃描螢幕上的 QR 碼...
|
||||
Availability Statistics=統計可用性
|
||||
Show Logs...=顯示日誌...
|
||||
Verbose Logging=詳細記錄日誌
|
||||
Updates...=更新...
|
||||
Check for Updates...=檢查更新
|
||||
Check for Updates at Startup=啟動時檢查更新
|
||||
About...=關於...
|
||||
Quit=退出
|
||||
Edit Servers=編輯伺服器
|
||||
Load Balance=負載均衡
|
||||
High Availability=高可用
|
||||
Choose by statistics=根據統計
|
||||
|
||||
# Config Form
|
||||
|
||||
&Add=新增(&A)
|
||||
&Delete=移除(&D)
|
||||
Server=伺服器
|
||||
Server Addr=伺服器位址
|
||||
Server Port=伺服器連接埠
|
||||
Password=密碼
|
||||
Encryption=加密
|
||||
Proxy Port=代理連接埠
|
||||
Remarks=備註
|
||||
Onetime Authentication=單次驗證
|
||||
OK=確定
|
||||
Cancel=取消
|
||||
New server=未配置的伺服器
|
||||
Move &Up=上移(&U)
|
||||
Move D&own=下移(&O)
|
||||
|
||||
# Proxy Form
|
||||
|
||||
Edit Proxy=代理設置
|
||||
Use Proxy=使用代理
|
||||
Proxy Addr=代理位址
|
||||
Proxy Port=代理連接埠
|
||||
|
||||
# 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=出站
|
||||
|
||||
# QRCode Form
|
||||
|
||||
QRCode=QR 碼
|
||||
|
||||
# PAC Url Form
|
||||
|
||||
Edit Online PAC URL=編輯在線 PAC 網址
|
||||
Edit Online PAC URL...=編輯在線 PAC 網址...
|
||||
Please input PAC Url=請輸入 PAC 網址
|
||||
|
||||
# Messages
|
||||
|
||||
Shadowsocks Error: {0}=Shadowsocks 錯誤: {0}
|
||||
Port already in use=連接埠號碼已被占用
|
||||
Illegal port number format=非法連接埠號碼格式
|
||||
Please add at least one server=請新增至少壹個伺服器
|
||||
Server IP can not be blank=伺服器 IP 不能為空
|
||||
Password can not be blank=密碼不能為空
|
||||
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 文件失敗
|
||||
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 碼
|
||||
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。
|
||||
Proxy request failed=代理請求失敗
|
||||
Proxy handshake failed=代理握手失敗
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
|
||||
namespace Shadowsocks.Encryption
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Shadowsocks.Encryption
|
||||
{
|
||||
public static class EncryptorFactory
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Shadowsocks.Encryption
|
||||
{
|
||||
|
||||
@@ -14,14 +14,6 @@ namespace Shadowsocks.Encryption
|
||||
public const int MAX_KEY_LENGTH = 64;
|
||||
public const int MAX_IV_LENGTH = 16;
|
||||
|
||||
public const int ONETIMEAUTH_FLAG = 0x10;
|
||||
public const int ADDRTYPE_MASK = 0xF;
|
||||
|
||||
public const int ONETIMEAUTH_BYTES = 10;
|
||||
|
||||
public const int CLEN_BYTES = 2;
|
||||
public const int AUTH_BYTES = ONETIMEAUTH_BYTES + CLEN_BYTES;
|
||||
|
||||
protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE];
|
||||
|
||||
protected Dictionary<string, Dictionary<string, int[]>> ciphers;
|
||||
@@ -40,8 +32,6 @@ namespace Shadowsocks.Encryption
|
||||
protected byte[] _key;
|
||||
protected int keyLen;
|
||||
protected int ivLen;
|
||||
protected uint counter = 0;
|
||||
protected byte[] _keyBuffer = null;
|
||||
|
||||
public IVEncryptor(string method, string password, bool onetimeauth, bool isudp)
|
||||
: base(method, password, onetimeauth, isudp)
|
||||
@@ -51,7 +41,7 @@ namespace Shadowsocks.Encryption
|
||||
|
||||
protected abstract Dictionary<string, Dictionary<string, int[]>> getCiphers();
|
||||
|
||||
protected void InitKey(string method, string password)
|
||||
private void InitKey(string method, string password)
|
||||
{
|
||||
method = method.ToLower();
|
||||
_method = method;
|
||||
@@ -71,7 +61,6 @@ namespace Shadowsocks.Encryption
|
||||
{
|
||||
byte[] passbuf = Encoding.UTF8.GetBytes(password);
|
||||
byte[] key = new byte[32];
|
||||
byte[] iv = new byte[16];
|
||||
bytesToKey(passbuf, key);
|
||||
return key;
|
||||
});
|
||||
@@ -99,14 +88,6 @@ namespace Shadowsocks.Encryption
|
||||
}
|
||||
}
|
||||
|
||||
protected static void randBytes(byte[] buf, int length)
|
||||
{
|
||||
byte[] temp = new byte[length];
|
||||
RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider();
|
||||
rngServiceProvider.GetBytes(temp);
|
||||
temp.CopyTo(buf, 0);
|
||||
}
|
||||
|
||||
protected virtual void initCipher(byte[] iv, bool isCipher)
|
||||
{
|
||||
if (ivLen > 0)
|
||||
@@ -126,7 +107,20 @@ namespace Shadowsocks.Encryption
|
||||
|
||||
protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf);
|
||||
|
||||
protected int getHeadLen(byte[] buf, int length)
|
||||
#region OneTimeAuth
|
||||
|
||||
public const int ONETIMEAUTH_FLAG = 0x10;
|
||||
public const int ADDRTYPE_MASK = 0xF;
|
||||
|
||||
public const int ONETIMEAUTH_BYTES = 10;
|
||||
|
||||
public const int CLEN_BYTES = 2;
|
||||
public const int AUTH_BYTES = ONETIMEAUTH_BYTES + CLEN_BYTES;
|
||||
|
||||
private uint _otaChunkCounter;
|
||||
private byte[] _otaChunkKeyBuffer;
|
||||
|
||||
protected int OtaGetHeadLen(byte[] buf, int length)
|
||||
{
|
||||
int len = 0;
|
||||
int atyp = length > 0 ? (buf[0] & ADDRTYPE_MASK) : 0;
|
||||
@@ -148,7 +142,7 @@ namespace Shadowsocks.Encryption
|
||||
return len;
|
||||
}
|
||||
|
||||
protected byte[] genOnetimeAuthHash(byte[] msg, int msg_len)
|
||||
private byte[] OtaGenHash(byte[] msg, int msg_len)
|
||||
{
|
||||
byte[] auth = new byte[ONETIMEAUTH_BYTES];
|
||||
byte[] hash = new byte[20];
|
||||
@@ -161,47 +155,55 @@ namespace Shadowsocks.Encryption
|
||||
return auth;
|
||||
}
|
||||
|
||||
protected void updateKeyBuffer()
|
||||
private void OtaUpdateKeyBuffer()
|
||||
{
|
||||
if (_keyBuffer == null)
|
||||
if (_otaChunkKeyBuffer == null)
|
||||
{
|
||||
_keyBuffer = new byte[MAX_IV_LENGTH + 4];
|
||||
Buffer.BlockCopy(_encryptIV, 0, _keyBuffer, 0, ivLen);
|
||||
_otaChunkKeyBuffer = new byte[MAX_IV_LENGTH + 4];
|
||||
Buffer.BlockCopy(_encryptIV, 0, _otaChunkKeyBuffer, 0, ivLen);
|
||||
}
|
||||
|
||||
byte[] counter_bytes = BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder((int)counter));
|
||||
Buffer.BlockCopy(counter_bytes, 0, _keyBuffer, ivLen, 4);
|
||||
counter++;
|
||||
byte[] counter_bytes = BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder((int)_otaChunkCounter));
|
||||
Buffer.BlockCopy(counter_bytes, 0, _otaChunkKeyBuffer, ivLen, 4);
|
||||
_otaChunkCounter++;
|
||||
}
|
||||
|
||||
protected byte[] genHash(byte[] buf, int offset, int len)
|
||||
private byte[] OtaGenChunkHash(byte[] buf, int offset, int len)
|
||||
{
|
||||
byte[] hash = new byte[20];
|
||||
updateKeyBuffer();
|
||||
Sodium.ss_sha1_hmac_ex(_keyBuffer, (uint)_keyBuffer.Length,
|
||||
OtaUpdateKeyBuffer();
|
||||
Sodium.ss_sha1_hmac_ex(_otaChunkKeyBuffer, (uint)_otaChunkKeyBuffer.Length,
|
||||
buf, offset, (uint)len, hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
protected void reactBuffer4TCP(byte[] buf, ref int length)
|
||||
private void OtaAuthBuffer4Tcp(byte[] buf, ref int length)
|
||||
{
|
||||
if (!_encryptIVSent)
|
||||
{
|
||||
int headLen = getHeadLen(buf, length);
|
||||
int headLen = OtaGetHeadLen(buf, length);
|
||||
int dataLen = length - headLen;
|
||||
buf[0] |= ONETIMEAUTH_FLAG;
|
||||
byte[] hash = genOnetimeAuthHash(buf, headLen);
|
||||
byte[] hash = OtaGenHash(buf, headLen);
|
||||
Buffer.BlockCopy(buf, headLen, buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen);
|
||||
Buffer.BlockCopy(hash, 0, buf, headLen, ONETIMEAUTH_BYTES);
|
||||
hash = genHash(buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen);
|
||||
Buffer.BlockCopy(hash, 0, buf, headLen + ONETIMEAUTH_BYTES + CLEN_BYTES, ONETIMEAUTH_BYTES);
|
||||
byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)dataLen));
|
||||
Buffer.BlockCopy(lenBytes, 0, buf, headLen + ONETIMEAUTH_BYTES, CLEN_BYTES);
|
||||
length = headLen + ONETIMEAUTH_BYTES + AUTH_BYTES + dataLen;
|
||||
|
||||
if (dataLen == 0)
|
||||
{
|
||||
length = headLen + ONETIMEAUTH_BYTES;
|
||||
}
|
||||
else
|
||||
{
|
||||
hash = OtaGenChunkHash(buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen);
|
||||
Buffer.BlockCopy(hash, 0, buf, headLen + ONETIMEAUTH_BYTES + CLEN_BYTES, ONETIMEAUTH_BYTES);
|
||||
byte[] lenBytes = BitConverter.GetBytes((ushort) IPAddress.HostToNetworkOrder((short) dataLen));
|
||||
Buffer.BlockCopy(lenBytes, 0, buf, headLen + ONETIMEAUTH_BYTES, CLEN_BYTES);
|
||||
length = headLen + ONETIMEAUTH_BYTES + AUTH_BYTES + dataLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] hash = genHash(buf, 0, length);
|
||||
byte[] hash = OtaGenChunkHash(buf, 0, length);
|
||||
Buffer.BlockCopy(buf, 0, buf, AUTH_BYTES, length);
|
||||
byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)length));
|
||||
Buffer.BlockCopy(lenBytes, 0, buf, 0, CLEN_BYTES);
|
||||
@@ -210,37 +212,48 @@ namespace Shadowsocks.Encryption
|
||||
}
|
||||
}
|
||||
|
||||
protected void reactBuffer4UDP(byte[] buf, ref int length)
|
||||
private void OtaAuthBuffer4Udp(byte[] buf, ref int length)
|
||||
{
|
||||
buf[0] |= ONETIMEAUTH_FLAG;
|
||||
byte[] hash = genOnetimeAuthHash(buf, length);
|
||||
byte[] hash = OtaGenHash(buf, length);
|
||||
Buffer.BlockCopy(hash, 0, buf, length, ONETIMEAUTH_BYTES);
|
||||
length += ONETIMEAUTH_BYTES;
|
||||
}
|
||||
|
||||
protected void reactBuffer(byte[] buf, ref int length)
|
||||
private void OtaAuthBuffer(byte[] buf, ref int length)
|
||||
{
|
||||
if (OnetimeAuth && ivLen > 0)
|
||||
{
|
||||
if (!IsUDP)
|
||||
{
|
||||
reactBuffer4TCP(buf, ref length);
|
||||
OtaAuthBuffer4Tcp(buf, ref length);
|
||||
}
|
||||
else
|
||||
{
|
||||
reactBuffer4UDP(buf, ref length);
|
||||
OtaAuthBuffer4Udp(buf, ref length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected static void randBytes(byte[] buf, int length)
|
||||
{
|
||||
byte[] temp = new byte[length];
|
||||
RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider();
|
||||
rngServiceProvider.GetBytes(temp);
|
||||
temp.CopyTo(buf, 0);
|
||||
}
|
||||
|
||||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
|
||||
{
|
||||
if (!_encryptIVSent)
|
||||
{
|
||||
// Generate IV
|
||||
randBytes(outbuf, ivLen);
|
||||
initCipher(outbuf, true);
|
||||
outlength = length + ivLen;
|
||||
reactBuffer(buf, ref length);
|
||||
OtaAuthBuffer(buf, ref length);
|
||||
_encryptIVSent = true;
|
||||
lock (tempbuf)
|
||||
{
|
||||
@@ -251,7 +264,7 @@ namespace Shadowsocks.Encryption
|
||||
}
|
||||
else
|
||||
{
|
||||
reactBuffer(buf, ref length);
|
||||
OtaAuthBuffer(buf, ref length);
|
||||
outlength = length;
|
||||
cipherUpdate(true, length, buf, outbuf);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Shadowsocks.Encryption
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int cipher_setup(IntPtr ctx, IntPtr cipher_info);
|
||||
|
||||
// check operation
|
||||
// XXX: Check operation before using it
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int cipher_setkey(IntPtr ctx, byte[] key, int key_bitlen, int operation);
|
||||
|
||||
@@ -70,9 +70,6 @@ namespace Shadowsocks.Encryption
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void md5(byte[] input, uint ilen, byte[] output);
|
||||
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void cipher_set_operation_ex(IntPtr ctx, int operation);
|
||||
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int cipher_get_size_ex();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Shadowsocks.Encryption
|
||||
{
|
||||
@@ -21,7 +18,6 @@ namespace Shadowsocks.Encryption
|
||||
public MbedTLSEncryptor(string method, string password, bool onetimeauth, bool isudp)
|
||||
: base(method, password, onetimeauth, isudp)
|
||||
{
|
||||
InitKey(method, password);
|
||||
}
|
||||
|
||||
private static Dictionary<string, Dictionary<string, int[]>> _ciphers = new Dictionary<string, Dictionary<string, int[]>> {
|
||||
@@ -75,15 +71,25 @@ namespace Shadowsocks.Encryption
|
||||
}
|
||||
MbedTLS.cipher_init(ctx);
|
||||
if (MbedTLS.cipher_setup( ctx, MbedTLS.cipher_info_from_string( _cipherMbedName ) ) != 0 )
|
||||
throw new Exception();
|
||||
// MbedTLS takes key length by bit
|
||||
// cipher_setkey() will set the correct key schedule
|
||||
if (MbedTLS.cipher_setkey(ctx, realkey, keyLen * 8, isCipher ? MbedTLS.MBEDTLS_ENCRYPT : MbedTLS.MBEDTLS_DECRYPT) != 0 )
|
||||
throw new Exception();
|
||||
throw new Exception("Cannot initialize mbed TLS cipher context");
|
||||
/*
|
||||
* MbedTLS takes key length by bit
|
||||
* cipher_setkey() will set the correct key schedule
|
||||
* and operation
|
||||
*
|
||||
* MBEDTLS_AES_{EN,DE}CRYPT
|
||||
* == MBEDTLS_BLOWFISH_{EN,DE}CRYPT
|
||||
* == MBEDTLS_CAMELLIA_{EN,DE}CRYPT
|
||||
* == MBEDTLS_{EN,DE}CRYPT
|
||||
*
|
||||
*/
|
||||
if (MbedTLS.cipher_setkey(ctx, realkey, keyLen * 8,
|
||||
isCipher ? MbedTLS.MBEDTLS_ENCRYPT : MbedTLS.MBEDTLS_DECRYPT) != 0 )
|
||||
throw new Exception("Cannot set mbed TLS cipher key");
|
||||
if (MbedTLS.cipher_set_iv(ctx, iv, ivLen) != 0)
|
||||
throw new Exception();
|
||||
throw new Exception("Cannot set mbed TLS cipher IV");
|
||||
if (MbedTLS.cipher_reset(ctx) != 0)
|
||||
throw new Exception();
|
||||
throw new Exception("Cannot finalize mbed TLS cipher context");
|
||||
}
|
||||
|
||||
protected override void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf)
|
||||
@@ -93,34 +99,9 @@ namespace Shadowsocks.Encryption
|
||||
{
|
||||
throw new ObjectDisposedException(this.ToString());
|
||||
}
|
||||
IntPtr ctx;
|
||||
if (isCipher)
|
||||
{
|
||||
ctx = _encryptCtx;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx = _decryptCtx;
|
||||
}
|
||||
|
||||
if (_cipher == CIPHER_AES
|
||||
|| _cipher == CIPHER_BLOWFISH
|
||||
|| _cipher == CIPHER_CAMELLIA)
|
||||
{
|
||||
/*
|
||||
* operation workaround
|
||||
*
|
||||
* MBEDTLS_AES_{EN,DE}CRYPT
|
||||
* == MBEDTLS_BLOWFISH_{EN,DE}CRYPT
|
||||
* == MBEDTLS_CAMELLIA_{EN,DE}CRYPT
|
||||
* == MBEDTLS_{EN,DE}CRYPT
|
||||
* setter code in C:
|
||||
* ctx->operation = operation;
|
||||
*/
|
||||
MbedTLS.cipher_set_operation_ex(ctx, isCipher ? MbedTLS.MBEDTLS_ENCRYPT : MbedTLS.MBEDTLS_DECRYPT);
|
||||
}
|
||||
if (MbedTLS.cipher_update(ctx, buf, length, outbuf, ref length) != 0 )
|
||||
throw new Exception();
|
||||
if (MbedTLS.cipher_update(isCipher ? _encryptCtx : _decryptCtx,
|
||||
buf, length, outbuf, ref length) != 0 )
|
||||
throw new Exception("Cannot update mbed TLS cipher context");
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
@@ -33,16 +33,16 @@ namespace Shadowsocks.Encryption
|
||||
private static extern IntPtr LoadLibrary(string path);
|
||||
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public extern static int crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k);
|
||||
public static extern int crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k);
|
||||
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public extern static int crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k);
|
||||
public static extern int crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k);
|
||||
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public extern static int crypto_stream_chacha20_ietf_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, uint ic, byte[] k);
|
||||
public static extern int crypto_stream_chacha20_ietf_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, uint ic, byte[] k);
|
||||
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen,
|
||||
public static extern void ss_sha1_hmac_ex(byte[] key, uint keylen,
|
||||
byte[] input, int ioff, uint ilen,
|
||||
byte[] output);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Shadowsocks.Encryption
|
||||
{
|
||||
@@ -24,7 +22,6 @@ namespace Shadowsocks.Encryption
|
||||
public SodiumEncryptor(string method, string password, bool onetimeauth, bool isudp)
|
||||
: base(method, password, onetimeauth, isudp)
|
||||
{
|
||||
InitKey(method, password);
|
||||
}
|
||||
|
||||
private static Dictionary<string, Dictionary<string, int[]>> _ciphers = new Dictionary<string, Dictionary<string, int[]>> {
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Shadowsocks.Model
|
||||
public class Server
|
||||
{
|
||||
public static readonly Regex
|
||||
UrlFinder = new Regex("^ss://((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$",
|
||||
UrlFinder = new Regex("^ss://((?:[A-Za-z0-9+/]+)|((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?))$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
||||
DetailsParser = new Regex("^((?<method>.+?)(?<auth>-auth)??:(?<password>.*)@(?<hostname>.+?)" +
|
||||
":(?<port>\\d+?))$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
@@ -18,6 +18,14 @@ namespace Shadowsocks
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
// Check OS since we are using dual-mode socket
|
||||
if (!Utils.IsWinVistaOrHigher())
|
||||
{
|
||||
MessageBox.Show(I18N.GetString("Unsupported operating system, use Windows Vista at least."),
|
||||
"Shadowsocks Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.ReleaseMemory(true);
|
||||
using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode()))
|
||||
{
|
||||
|
||||
+13
@@ -100,6 +100,19 @@ namespace Shadowsocks.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to # translation for Traditional Chinese
|
||||
///
|
||||
///Shadowsocks=Shadowsocks
|
||||
///
|
||||
///# Menu items
|
||||
/// </summary>
|
||||
internal static string zh_tw {
|
||||
get {
|
||||
return ResourceManager.GetString("zh_tw", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
|
||||
@@ -160,4 +160,7 @@
|
||||
<data name="user_rule" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\data\user-rule.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
<data name="zh_tw" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Data\zh_tw.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Proxy
|
||||
{
|
||||
@@ -20,19 +21,26 @@ namespace Shadowsocks.Proxy
|
||||
public bool CompletedSynchronously { get; } = true;
|
||||
}
|
||||
|
||||
private class FakeEndPoint : EndPoint
|
||||
{
|
||||
public override AddressFamily AddressFamily { get; } = AddressFamily.Unspecified;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "null proxy";
|
||||
}
|
||||
}
|
||||
|
||||
private Socket _remote;
|
||||
|
||||
public EndPoint LocalEndPoint => _remote.LocalEndPoint;
|
||||
|
||||
public EndPoint ProxyEndPoint { get; private set; }
|
||||
|
||||
public EndPoint ProxyEndPoint { get; } = new FakeEndPoint();
|
||||
public EndPoint DestEndPoint { get; private set; }
|
||||
|
||||
|
||||
public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
|
||||
{
|
||||
// do nothing
|
||||
ProxyEndPoint = remoteEP;
|
||||
|
||||
var r = new FakeAsyncResult(state);
|
||||
callback?.Invoke(r);
|
||||
@@ -43,22 +51,16 @@ namespace Shadowsocks.Proxy
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state)
|
||||
public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state)
|
||||
{
|
||||
if (_remote == null)
|
||||
{
|
||||
_remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
||||
}
|
||||
DestEndPoint = destEndPoint;
|
||||
|
||||
DestEndPoint = remoteEP;
|
||||
|
||||
_remote.BeginConnect(remoteEP, callback, state);
|
||||
SocketUtil.BeginConnectTcp(destEndPoint, callback, state);
|
||||
}
|
||||
|
||||
public void EndConnectDest(IAsyncResult asyncResult)
|
||||
{
|
||||
_remote?.EndConnect(asyncResult);
|
||||
_remote = SocketUtil.EndConnectTcp(asyncResult);
|
||||
}
|
||||
|
||||
public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Shadowsocks.Proxy
|
||||
|
||||
void EndConnectProxy(IAsyncResult asyncResult);
|
||||
|
||||
void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state);
|
||||
void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state);
|
||||
|
||||
void EndConnectDest(IAsyncResult asyncResult);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Shadowsocks.Controller;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Proxy
|
||||
{
|
||||
@@ -51,16 +52,13 @@ namespace Shadowsocks.Proxy
|
||||
|
||||
public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
|
||||
{
|
||||
_remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
||||
|
||||
var st = new Socks5State();
|
||||
st.Callback = callback;
|
||||
st.AsyncState = state;
|
||||
|
||||
ProxyEndPoint = remoteEP;
|
||||
|
||||
_remote.BeginConnect(remoteEP, ConnectCallback, st);
|
||||
SocketUtil.BeginConnectTcp(remoteEP, ConnectCallback, st);
|
||||
}
|
||||
|
||||
public void EndConnectProxy(IAsyncResult asyncResult)
|
||||
@@ -73,50 +71,62 @@ namespace Shadowsocks.Proxy
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state)
|
||||
public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state)
|
||||
{
|
||||
var ep = remoteEP as IPEndPoint;
|
||||
if (ep == null)
|
||||
{
|
||||
throw new Exception(I18N.GetString("Proxy request faild"));
|
||||
}
|
||||
DestEndPoint = destEndPoint;
|
||||
|
||||
byte[] request = null;
|
||||
byte atyp = 0;
|
||||
switch (ep.AddressFamily)
|
||||
int port;
|
||||
|
||||
var dep = destEndPoint as DnsEndPoint;
|
||||
if (dep != null)
|
||||
{
|
||||
case AddressFamily.InterNetwork:
|
||||
request = new byte[4 + 4 + 2];
|
||||
atyp = 1;
|
||||
break;
|
||||
case AddressFamily.InterNetworkV6:
|
||||
request = new byte[4 + 16 + 2];
|
||||
atyp = 4;
|
||||
break;
|
||||
// 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;
|
||||
}
|
||||
if (request == null)
|
||||
else
|
||||
{
|
||||
throw new Exception(I18N.GetString("Proxy request faild"));
|
||||
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包
|
||||
var addr = ep.Address.GetAddressBytes();
|
||||
// 构造request包剩余部分
|
||||
request[0] = 5;
|
||||
request[1] = 1;
|
||||
request[2] = 0;
|
||||
request[3] = atyp;
|
||||
Array.Copy(addr, 0, request, 4, request.Length - 4 - 2);
|
||||
request[request.Length - 2] = (byte) ((ep.Port >> 8) & 0xff);
|
||||
request[request.Length - 1] = (byte) (ep.Port & 0xff);
|
||||
request[request.Length - 2] = (byte) ((port >> 8) & 0xff);
|
||||
request[request.Length - 1] = (byte) (port & 0xff);
|
||||
|
||||
var st = new Socks5State();
|
||||
st.Callback = callback;
|
||||
st.AsyncState = state;
|
||||
|
||||
DestEndPoint = remoteEP;
|
||||
|
||||
_remote?.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, st);
|
||||
|
||||
}
|
||||
|
||||
public void EndConnectDest(IAsyncResult asyncResult)
|
||||
@@ -167,7 +177,7 @@ namespace Shadowsocks.Proxy
|
||||
var state = (Socks5State) ar.AsyncState;
|
||||
try
|
||||
{
|
||||
_remote.EndConnect(ar);
|
||||
_remote = SocketUtil.EndConnectTcp(ar);
|
||||
|
||||
byte[] handshake = {5, 1, 0};
|
||||
_remote.BeginSend(handshake, 0, handshake.Length, 0, Socks5HandshakeSendCallback, state);
|
||||
@@ -206,12 +216,12 @@ namespace Shadowsocks.Proxy
|
||||
{
|
||||
if (_receiveBuffer[0] != 5 || _receiveBuffer[1] != 0)
|
||||
{
|
||||
ex = new Exception(I18N.GetString("Proxy handshake faild"));
|
||||
ex = new Exception(I18N.GetString("Proxy handshake failed"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ex = new Exception(I18N.GetString("Proxy handshake faild"));
|
||||
ex = new Exception(I18N.GetString("Proxy handshake failed"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex2)
|
||||
@@ -261,20 +271,20 @@ namespace Shadowsocks.Proxy
|
||||
_remote.BeginReceive(_receiveBuffer, 0, 16 + 2, 0, Socks5ReplyReceiveCallback2, state);
|
||||
break;
|
||||
default:
|
||||
state.ex = new Exception(I18N.GetString("Proxy request faild"));
|
||||
state.ex = new Exception(I18N.GetString("Proxy request failed"));
|
||||
state.Callback?.Invoke(new FakeAsyncResult(ar, state));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.ex = new Exception(I18N.GetString("Proxy request faild"));
|
||||
state.ex = new Exception(I18N.GetString("Proxy request failed"));
|
||||
state.Callback?.Invoke(new FakeAsyncResult(ar, state));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.ex = new Exception(I18N.GetString("Proxy request faild"));
|
||||
state.ex = new Exception(I18N.GetString("Proxy request failed"));
|
||||
state.Callback?.Invoke(new FakeAsyncResult(ar, state));
|
||||
}
|
||||
}
|
||||
@@ -297,7 +307,7 @@ namespace Shadowsocks.Proxy
|
||||
|
||||
if (bytesRead < bytesNeedSkip)
|
||||
{
|
||||
ex = new Exception(I18N.GetString("Proxy request faild"));
|
||||
ex = new Exception(I18N.GetString("Proxy request failed"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex2)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 224 B After Width: | Height: | Size: 225 B |
Binary file not shown.
|
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 239 B |
@@ -0,0 +1,154 @@
|
||||
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
|
||||
{
|
||||
[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);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
private IntPtr handle;
|
||||
private bool disposed;
|
||||
|
||||
public Job()
|
||||
{
|
||||
handle = CreateJobObject(IntPtr.Zero, null);
|
||||
|
||||
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
|
||||
{
|
||||
LimitFlags = 0x2000
|
||||
};
|
||||
|
||||
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||
{
|
||||
BasicLimitInformation = info
|
||||
};
|
||||
|
||||
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
|
||||
IntPtr 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()));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
if (disposing) { }
|
||||
|
||||
Close();
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
CloseHandle(handle);
|
||||
handle = IntPtr.Zero;
|
||||
}
|
||||
|
||||
public bool AddProcess(IntPtr processHandle)
|
||||
{
|
||||
var succ = AssignProcessToJobObject(handle, processHandle);
|
||||
|
||||
if (!succ)
|
||||
{
|
||||
var err = Marshal.GetLastWin32Error();
|
||||
Logging.Error("Failed to call AssignProcessToJobObject! GetLastError=" + err);
|
||||
}
|
||||
|
||||
return succ;
|
||||
}
|
||||
|
||||
public bool AddProcess(int processId)
|
||||
{
|
||||
return AddProcess(Process.GetProcessById(processId).Handle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace Shadowsocks.Util
|
||||
{
|
||||
public static class SocketUtil
|
||||
{
|
||||
private class DnsEndPoint2 : DnsEndPoint
|
||||
{
|
||||
public DnsEndPoint2(string host, int port) : base(host, port)
|
||||
{
|
||||
}
|
||||
|
||||
public DnsEndPoint2(string host, int port, AddressFamily addressFamily) : base(host, port, addressFamily)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Host + ":" + this.Port;
|
||||
}
|
||||
}
|
||||
|
||||
public static EndPoint GetEndPoint(string host, int port)
|
||||
{
|
||||
IPAddress ipAddress;
|
||||
bool parsed = IPAddress.TryParse(host, out ipAddress);
|
||||
if (parsed)
|
||||
{
|
||||
return new IPEndPoint(ipAddress, port);
|
||||
}
|
||||
|
||||
// maybe is a domain name
|
||||
return new DnsEndPoint2(host, port);
|
||||
}
|
||||
|
||||
private class AutoReleaseAsyncResult : IAsyncResult
|
||||
{
|
||||
|
||||
public bool IsCompleted { get; } = true;
|
||||
public WaitHandle AsyncWaitHandle { get; } = null;
|
||||
public object AsyncState { get; set; }
|
||||
public bool CompletedSynchronously { get; } = true;
|
||||
|
||||
public TcpUserToken UserToken { get; set; }
|
||||
|
||||
~AutoReleaseAsyncResult()
|
||||
{
|
||||
UserToken.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private class TcpUserToken
|
||||
{
|
||||
public AsyncCallback Callback { get; private set; }
|
||||
public SocketAsyncEventArgs Args { get; private set; }
|
||||
public object AsyncState { get; private set; }
|
||||
|
||||
public TcpUserToken(AsyncCallback callback, object state, SocketAsyncEventArgs args)
|
||||
{
|
||||
Callback = callback;
|
||||
AsyncState = state;
|
||||
Args = args;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Args?.Dispose();
|
||||
Callback = null;
|
||||
Args = null;
|
||||
AsyncState = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnTcpConnectCompleted(object sender, SocketAsyncEventArgs args)
|
||||
{
|
||||
args.Completed -= OnTcpConnectCompleted;
|
||||
TcpUserToken token = (TcpUserToken) args.UserToken;
|
||||
|
||||
AutoReleaseAsyncResult r = new AutoReleaseAsyncResult
|
||||
{
|
||||
AsyncState = token.AsyncState,
|
||||
UserToken = token
|
||||
};
|
||||
|
||||
token.Callback(r);
|
||||
}
|
||||
|
||||
public static void BeginConnectTcp(EndPoint endPoint, AsyncCallback callback, object state)
|
||||
{
|
||||
var arg = new SocketAsyncEventArgs();
|
||||
arg.RemoteEndPoint = endPoint;
|
||||
arg.Completed += OnTcpConnectCompleted;
|
||||
arg.UserToken = new TcpUserToken(callback, state, arg);
|
||||
|
||||
|
||||
Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, arg);
|
||||
}
|
||||
|
||||
public static Socket EndConnectTcp(IAsyncResult asyncResult)
|
||||
{
|
||||
var r = asyncResult as AutoReleaseAsyncResult;
|
||||
if (r == null)
|
||||
{
|
||||
throw new ArgumentException("Invalid asyncResult.", nameof(asyncResult));
|
||||
}
|
||||
|
||||
var tut = r.UserToken;
|
||||
|
||||
var arg = tut.Args;
|
||||
|
||||
if (arg.SocketError != SocketError.Success)
|
||||
{
|
||||
if (arg.ConnectByNameError != null)
|
||||
{
|
||||
throw arg.ConnectByNameError;
|
||||
}
|
||||
|
||||
var ex = new SocketException((int)arg.SocketError);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
var so = tut.Args.ConnectSocket;
|
||||
|
||||
so.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
|
||||
|
||||
return so;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,25 @@ namespace Shadowsocks.Util
|
||||
{
|
||||
public 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;
|
||||
}
|
||||
|
||||
// return path to store temporary files
|
||||
public static string GetTempPath()
|
||||
{
|
||||
if (TempPath == null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(Application.StartupPath, "shadowsocks_portable_mode.txt")))
|
||||
if (IsPortableMode())
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "temp"));
|
||||
@@ -106,6 +117,44 @@ namespace Shadowsocks.Util
|
||||
return $"{result.Item1:0.##}{result.Item2}";
|
||||
}
|
||||
|
||||
public static string FormatBytes(long bytes)
|
||||
{
|
||||
const long K = 1024L;
|
||||
const long M = K * 1024L;
|
||||
const long G = M * 1024L;
|
||||
const long T = G * 1024L;
|
||||
const long P = T * 1024L;
|
||||
const long E = P * 1024L;
|
||||
|
||||
if (bytes >= P * 990)
|
||||
return (bytes / (double)E).ToString("F5") + "EB";
|
||||
if (bytes >= T * 990)
|
||||
return (bytes / (double)P).ToString("F5") + "PB";
|
||||
if (bytes >= G * 990)
|
||||
return (bytes / (double)T).ToString("F5") + "TB";
|
||||
if (bytes >= M * 990)
|
||||
{
|
||||
return (bytes / (double)G).ToString("F4") + "GB";
|
||||
}
|
||||
if (bytes >= M * 100)
|
||||
{
|
||||
return (bytes / (double)M).ToString("F1") + "MB";
|
||||
}
|
||||
if (bytes >= M * 10)
|
||||
{
|
||||
return (bytes / (double)M).ToString("F2") + "MB";
|
||||
}
|
||||
if (bytes >= K * 990)
|
||||
{
|
||||
return (bytes / (double)M).ToString("F3") + "MB";
|
||||
}
|
||||
if (bytes > K * 2)
|
||||
{
|
||||
return (bytes / (double)K).ToString("F1") + "KB";
|
||||
}
|
||||
return bytes.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return scaled bandwidth
|
||||
/// </summary>
|
||||
@@ -151,12 +200,16 @@ namespace Shadowsocks.Util
|
||||
// we are building x86 binary for both x86 and x64, which will
|
||||
// cause problem when opening registry key
|
||||
// detect operating system instead of CPU
|
||||
RegistryKey userKey = RegistryKey.OpenBaseKey( RegistryHive.CurrentUser,
|
||||
Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32 );
|
||||
userKey = userKey.OpenSubKey( name, writable );
|
||||
RegistryKey userKey = RegistryKey.OpenRemoteBaseKey( RegistryHive.CurrentUser, "",
|
||||
Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32 )
|
||||
.OpenSubKey( name, writable );
|
||||
return userKey;
|
||||
}
|
||||
|
||||
public static bool IsWinVistaOrHigher() {
|
||||
return Environment.OSVersion.Version.Major > 5;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool SetProcessWorkingSetSize(IntPtr process,
|
||||
|
||||
+3
-3
@@ -176,8 +176,8 @@
|
||||
this.PasswordTextBox.Name = "PasswordTextBox";
|
||||
this.PasswordTextBox.Size = new System.Drawing.Size(160, 21);
|
||||
this.PasswordTextBox.TabIndex = 2;
|
||||
this.PasswordTextBox.WordWrap = false;
|
||||
this.PasswordTextBox.UseSystemPasswordChar = true;
|
||||
this.PasswordTextBox.WordWrap = false;
|
||||
//
|
||||
// EncryptionLabel
|
||||
//
|
||||
@@ -211,12 +211,11 @@
|
||||
"bf-cfb",
|
||||
"camellia-128-cfb",
|
||||
"camellia-192-cfb",
|
||||
"camellia-256-cfb",});
|
||||
"camellia-256-cfb"});
|
||||
this.EncryptionSelect.Location = new System.Drawing.Point(83, 87);
|
||||
this.EncryptionSelect.Name = "EncryptionSelect";
|
||||
this.EncryptionSelect.Size = new System.Drawing.Size(160, 20);
|
||||
this.EncryptionSelect.TabIndex = 3;
|
||||
this.EncryptionSelect.SelectedIndexChanged += new System.EventHandler(this.EncryptionSelect_SelectedIndexChanged);
|
||||
//
|
||||
// OneTimeAuth
|
||||
//
|
||||
@@ -480,6 +479,7 @@
|
||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ConfigForm_FormClosed);
|
||||
this.Load += new System.EventHandler(this.ConfigForm_Load);
|
||||
this.Shown += new System.EventHandler(this.ConfigForm_Shown);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ConfigForm_KeyDown);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.ServerGroupBox.ResumeLayout(false);
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Shadowsocks.View
|
||||
EncryptionLabel.Text = I18N.GetString("Encryption");
|
||||
ProxyPortLabel.Text = I18N.GetString("Proxy Port");
|
||||
RemarksLabel.Text = I18N.GetString("Remarks");
|
||||
OneTimeAuth.Text = I18N.GetString("Onetime Authentication (Experimental)");
|
||||
OneTimeAuth.Text = I18N.GetString("Onetime Authentication");
|
||||
ServerGroupBox.Text = I18N.GetString("Server");
|
||||
OKButton.Text = I18N.GetString("OK");
|
||||
MyCancelButton.Text = I18N.GetString("Cancel");
|
||||
@@ -230,7 +230,6 @@ namespace Shadowsocks.View
|
||||
|
||||
private void OKButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Server server = controller.GetCurrentServer();
|
||||
if (!SaveOldSelectedServer())
|
||||
{
|
||||
return;
|
||||
@@ -241,7 +240,10 @@ namespace Shadowsocks.View
|
||||
return;
|
||||
}
|
||||
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort);
|
||||
controller.SelectServerIndex(_modifiedConfiguration.configs.IndexOf(server));
|
||||
// SelectedIndex remains valid
|
||||
// We handled this in event handlers, e.g. Add/DeleteButton, SelectedIndexChanged
|
||||
// and move operations
|
||||
controller.SelectServerIndex(ServersListBox.SelectedIndex);
|
||||
this.Close();
|
||||
}
|
||||
|
||||
@@ -325,18 +327,5 @@ namespace Shadowsocks.View
|
||||
MoveConfigItem(+1); // +1 means move forward
|
||||
}
|
||||
}
|
||||
|
||||
private void EncryptionSelect_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (EncryptionSelect.Text == "rc4" || EncryptionSelect.Text == "table")
|
||||
{
|
||||
OneTimeAuth.Enabled = false;
|
||||
OneTimeAuth.Checked = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
OneTimeAuth.Enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -250,6 +250,8 @@
|
||||
chartArea1.AxisX.MajorGrid.Interval = 5D;
|
||||
chartArea1.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;
|
||||
chartArea1.AxisX.MajorTickMark.Enabled = false;
|
||||
chartArea1.AxisX.Maximum = 61D;
|
||||
chartArea1.AxisX.Minimum = 1D;
|
||||
chartArea1.AxisY.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount;
|
||||
chartArea1.AxisY.LabelAutoFitMaxFontSize = 8;
|
||||
chartArea1.AxisY.LabelStyle.Interval = 0D;
|
||||
@@ -266,13 +268,17 @@
|
||||
this.trafficChart.Location = new System.Drawing.Point(0, 0);
|
||||
this.trafficChart.Name = "trafficChart";
|
||||
this.trafficChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.None;
|
||||
series1.BorderWidth = 2;
|
||||
series1.ChartArea = "ChartArea1";
|
||||
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
|
||||
series1.Color = System.Drawing.Color.FromArgb(255, 128, 0);
|
||||
series1.IsXValueIndexed = true;
|
||||
series1.Legend = "Legend1";
|
||||
series1.Name = "Inbound";
|
||||
series2.BorderWidth = 2;
|
||||
series2.ChartArea = "ChartArea1";
|
||||
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
|
||||
series2.Color = System.Drawing.Color.FromArgb(128, 128, 255);
|
||||
series2.IsXValueIndexed = true;
|
||||
series2.Legend = "Legend1";
|
||||
series2.Name = "Outbound";
|
||||
|
||||
@@ -22,12 +22,8 @@ namespace Shadowsocks.View
|
||||
ShadowsocksController controller;
|
||||
|
||||
#region chart
|
||||
List<float> inboundPoints = new List<float>();
|
||||
List<float> outboundPoints = new List<float>();
|
||||
long maxSpeed = 0;
|
||||
Tuple<float, string, long> bandwidthScale = new Tuple<float, string, long>(0, "B", 1);
|
||||
TextAnnotation inboundAnnotation = new TextAnnotation();
|
||||
TextAnnotation outboundAnnotation = new TextAnnotation();
|
||||
long lastMaxSpeed;
|
||||
public ShadowsocksController.QueueLast<Tuple<long, long>> traffic = new ShadowsocksController.QueueLast<Tuple<long, long>>();
|
||||
#endregion
|
||||
|
||||
public LogForm(ShadowsocksController controller, string filename)
|
||||
@@ -57,15 +53,40 @@ namespace Shadowsocks.View
|
||||
UpdateTexts();
|
||||
}
|
||||
|
||||
private void controller_TrafficChanged(object sender, EventArgs e)
|
||||
private void update_TrafficChart()
|
||||
{
|
||||
inboundPoints.Clear();
|
||||
outboundPoints.Clear();
|
||||
foreach (var trafficPerSecond in controller.traffic)
|
||||
List<float> inboundPoints = new List<float>();
|
||||
List<float> outboundPoints = new List<float>();
|
||||
TextAnnotation inboundAnnotation = new TextAnnotation();
|
||||
TextAnnotation outboundAnnotation = new TextAnnotation();
|
||||
Tuple<float, string, long> bandwidthScale;
|
||||
const long minScale = 50;
|
||||
long maxSpeed = 0;
|
||||
long lastInbound, lastOutbound;
|
||||
|
||||
lock (this)
|
||||
{
|
||||
inboundPoints.Add(trafficPerSecond.inboundIncreasement);
|
||||
outboundPoints.Add(trafficPerSecond.outboundIncreasement);
|
||||
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
|
||||
if (traffic.Count == 0)
|
||||
return;
|
||||
foreach (var trafficPerSecond in traffic)
|
||||
{
|
||||
inboundPoints.Add(trafficPerSecond.Item1);
|
||||
outboundPoints.Add(trafficPerSecond.Item2);
|
||||
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.Item1, trafficPerSecond.Item2));
|
||||
}
|
||||
lastInbound = traffic.Last().Item1;
|
||||
lastOutbound = traffic.Last().Item2;
|
||||
}
|
||||
|
||||
if (maxSpeed > 0)
|
||||
{
|
||||
lastMaxSpeed -= lastMaxSpeed / 32;
|
||||
maxSpeed = Math.Max(minScale, Math.Max(maxSpeed, lastMaxSpeed));
|
||||
lastMaxSpeed = maxSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxSpeed = lastMaxSpeed = minScale;
|
||||
}
|
||||
|
||||
bandwidthScale = Utils.GetBandwidthScale(maxSpeed);
|
||||
@@ -74,29 +95,31 @@ namespace Shadowsocks.View
|
||||
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
|
||||
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
|
||||
|
||||
try
|
||||
if (trafficChart.IsHandleCreated)
|
||||
{
|
||||
if (trafficChart.InvokeRequired && trafficChart.IsHandleCreated)
|
||||
{
|
||||
trafficChart.Invoke(new Action(() =>
|
||||
{
|
||||
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
|
||||
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
|
||||
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2;
|
||||
inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
|
||||
inboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.inboundIncreasement);
|
||||
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
|
||||
outboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.outboundIncreasement);
|
||||
trafficChart.Annotations.Clear();
|
||||
trafficChart.Annotations.Add(inboundAnnotation);
|
||||
trafficChart.Annotations.Add(outboundAnnotation);
|
||||
}));
|
||||
}
|
||||
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
|
||||
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
|
||||
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2;
|
||||
trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.Item1;
|
||||
inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
|
||||
inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound);
|
||||
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
|
||||
outboundAnnotation.Text = Utils.FormatBandwidth(lastOutbound);
|
||||
trafficChart.Annotations.Clear();
|
||||
trafficChart.Annotations.Add(inboundAnnotation);
|
||||
trafficChart.Annotations.Add(outboundAnnotation);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
}
|
||||
|
||||
private void controller_TrafficChanged(object sender, EventArgs e)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
// suppress the thread race error:
|
||||
// when closing the form but the Invoked Action is still working and cause 'Chart is Disposed' exception
|
||||
traffic = new ShadowsocksController.QueueLast<Tuple<long, long>>();
|
||||
foreach (var trafficPerSecond in controller.traffic)
|
||||
{
|
||||
traffic.Enqueue(new Tuple<long, long>(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +147,7 @@ namespace Shadowsocks.View
|
||||
private void Timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
UpdateContent();
|
||||
update_TrafficChart();
|
||||
}
|
||||
|
||||
private void InitContent()
|
||||
@@ -149,29 +173,35 @@ namespace Shadowsocks.View
|
||||
|
||||
private void UpdateContent()
|
||||
{
|
||||
try {
|
||||
using(StreamReader reader = new StreamReader(new FileStream(filename,
|
||||
FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) {
|
||||
try
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(new FileStream(filename,
|
||||
FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
{
|
||||
reader.BaseStream.Seek(lastOffset, SeekOrigin.Begin);
|
||||
|
||||
string line = "";
|
||||
bool changed = false;
|
||||
while((line = reader.ReadLine()) != null) {
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
changed = true;
|
||||
LogMessageTextBox.AppendText(line + Environment.NewLine);
|
||||
}
|
||||
|
||||
if(changed) {
|
||||
if (changed)
|
||||
{
|
||||
LogMessageTextBox.ScrollToCaret();
|
||||
}
|
||||
|
||||
lastOffset = reader.BaseStream.Position;
|
||||
}
|
||||
} catch(FileNotFoundException) {
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
|
||||
this.Text = I18N.GetString("Log Viewer") +
|
||||
$" [in: {Utils.FormatBandwidth(controller.inboundCounter)}, out: {Utils.FormatBandwidth(controller.outboundCounter)}]";
|
||||
$" [in: {Utils.FormatBytes(controller.InboundCounter)}, out: {Utils.FormatBytes(controller.OutboundCounter)}]";
|
||||
}
|
||||
|
||||
private void LogForm_Load(object sender, EventArgs e)
|
||||
@@ -179,7 +209,7 @@ namespace Shadowsocks.View
|
||||
InitContent();
|
||||
|
||||
timer = new Timer();
|
||||
timer.Interval = 300;
|
||||
timer.Interval = 100;
|
||||
timer.Tick += Timer_Tick;
|
||||
timer.Start();
|
||||
|
||||
@@ -190,7 +220,8 @@ namespace Shadowsocks.View
|
||||
Width = config.width;
|
||||
Top = config.GetBestTop();
|
||||
Left = config.GetBestLeft();
|
||||
if(config.maximized) {
|
||||
if (config.maximized)
|
||||
{
|
||||
WindowState = FormWindowState.Maximized;
|
||||
}
|
||||
|
||||
@@ -218,7 +249,8 @@ namespace Shadowsocks.View
|
||||
config.SetFont(LogMessageTextBox.Font);
|
||||
config.SetBackgroundColor(LogMessageTextBox.BackColor);
|
||||
config.SetTextColor(LogMessageTextBox.ForeColor);
|
||||
if(!(config.maximized = WindowState == FormWindowState.Maximized)) {
|
||||
if (WindowState != FormWindowState.Minimized && !(config.maximized = WindowState == FormWindowState.Maximized))
|
||||
{
|
||||
config.top = Top;
|
||||
config.left = Left;
|
||||
config.height = Height;
|
||||
@@ -247,7 +279,7 @@ namespace Shadowsocks.View
|
||||
#region Clean up the content in LogMessageTextBox.
|
||||
private void DoCleanLogs()
|
||||
{
|
||||
Logging.clear();
|
||||
Logging.Clear();
|
||||
lastOffset = 0;
|
||||
LogMessageTextBox.Clear();
|
||||
}
|
||||
|
||||
@@ -439,6 +439,7 @@ namespace Shadowsocks.View
|
||||
{
|
||||
configForm = new ConfigForm(controller);
|
||||
configForm.Show();
|
||||
configForm.Activate();
|
||||
configForm.FormClosed += configForm_FormClosed;
|
||||
}
|
||||
}
|
||||
@@ -453,6 +454,7 @@ namespace Shadowsocks.View
|
||||
{
|
||||
proxyForm = new ProxyForm(controller);
|
||||
proxyForm.Show();
|
||||
proxyForm.Activate();
|
||||
proxyForm.FormClosed += proxyForm_FormClosed;
|
||||
}
|
||||
}
|
||||
@@ -463,6 +465,7 @@ namespace Shadowsocks.View
|
||||
{
|
||||
LogForm f = new LogForm(controller, Logging.LogFilePath);
|
||||
f.Show();
|
||||
f.Activate();
|
||||
f.FormClosed += logForm_FormClosed;
|
||||
|
||||
logForms.Add(f);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" />
|
||||
<supportedRuntime version="v2.0.50727" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
|
||||
@@ -13,4 +13,17 @@
|
||||
<dpiAware>True/PM</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<!-- We specify these, in addition to the UAC above, so we avoid Program Compatibility Assistant in Vista and Win7 -->
|
||||
<!-- We try to avoid PCA so we can use Windows Job Objects -->
|
||||
<!-- See http://stackoverflow.com/questions/3342941/kill-child-process-when-parent-process-is-killed -->
|
||||
|
||||
<application>
|
||||
<!--The ID below indicates application support for Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!--The ID below indicates application support for Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
@@ -76,6 +76,7 @@
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Windows.Forms.DataVisualization" />
|
||||
@@ -176,6 +177,9 @@
|
||||
<Compile Include="Controller\Strategy\IStrategy.cs" />
|
||||
<Compile Include="Proxy\Socks5Proxy.cs" />
|
||||
<Compile Include="StringEx.cs" />
|
||||
<Compile Include="Util\ProcessManagement\Job.cs" />
|
||||
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" />
|
||||
<Compile Include="Util\SocketUtil.cs" />
|
||||
<Compile Include="Util\Util.cs" />
|
||||
<Compile Include="View\ConfigForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
@@ -267,6 +271,7 @@
|
||||
<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">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
|
||||
Reference in New Issue
Block a user