Compare commits

...
49 Commits
Author SHA1 Message Date
Syrone Wong 4b6d641d17 Update CHANGES and bump version
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-09 17:48:22 +08:00
破娃酱 59c315d465 new traffic chart style (#704)
* new traffic chart style
- remove invoke
- force to set AxisY.Maximum not zero
- format LogForm.cs

* avoid data race
2016-09-08 15:24:46 +08:00
Syrone Wong f8c9bdda12 Drop redundant type casting
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-08 08:20:12 +08:00
破娃酱 707442163d New traffic icon style (#703) 2016-09-07 20:00:31 +08:00
Syrone Wong bb7171674b Refine traditional Chinese translation again
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-07 10:08:56 +08:00
Syrone Wong 3b1ac7500f typo
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-07 09:54:35 +08:00
Syrone Wong 2bebe515fa Refine traditional Chinese translation
- Respect shadowsocks-android translation by @LNDDYL
- pretend to be native speaker

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-07 09:49:56 +08:00
Syrone Wong 4367a56f46 Drop .NET Framework lower than 4.0 explicitly
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-06 20:20:19 +08:00
破娃酱 d958c5c393 fix bandwidthScale of the LogForm (#701)
- Revert bandwidthScale when throughput gets smaller
- Don't save coordinates when LogForm is minimized. Otherwise, we get negative values
- activate ConfigForm & ProxyForm & LogForm
2016-09-06 18:46:35 +08:00
Syrone Wong c3b7c3dcfd Update AppVeyor
Added debug artifact for debugging purpose.

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-06 16:57:12 +08:00
破娃酱 d73b77ff88 fix ss URL bug with no padding base64 (#700) 2016-09-06 14:31:22 +08:00
破娃酱 da234ef857 add Traditional Chinese (#698)
* add Traditional Chinese

* fix a resource type

* fix the Traditional Chinese name of "address", "port", "screen", "Open Location"
2016-09-06 11:39:05 +08:00
Syrone Wong 514bb02098 Fix config selection
User wants to use that server after selected it in the ConfigForm. If user deletes
the server in use, we can't find its index, then the previous logic will revert to
index 0, which is not expected behaviour.

Since we handled 'ServersListBox.SelectedIndex' in various event handlers, we can
trust it all the time.

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-09-04 08:04:56 +08:00
noisyfox e364457b1b Fix a memory leak.
Caused by not call SocketAsyncEventArgs.Dispose().
2016-09-03 15:17:16 +08:00
noisyfox be85ab55a7 Fix #561 2016-09-03 08:58:13 +08:00
noisyfox e6e9fc8588 Refactor to make OTA code more independent and clear.
and misc changes
2016-09-03 08:57:33 +08:00
noisyfox 4c5376ce0b Remove unnecessary call to InitKey. 2016-09-03 08:57:31 +08:00
noisyfox 0266ad9d59 Remove redundant lock.
Lock is evil.

This will actually affect the accuracy of GetDelta because
inboundDelta and outboundDelta may be calculated based on
the data collected at different time (eg. UpdateOutbound
get called before Interlocked.Read(ref _outbound)).

But that's not a problem. We can tolerate this little
inaccuracy. Besides, the _inbound, _outbound, _lastInbound
and _lastOutbound will be totally good because we use Interlocked.
2016-09-03 08:57:30 +08:00
Syrone Wong cbc94e0eb7 Update privoxy to 3.0.26
Close #686

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-29 21:30:38 +08:00
Syrone Wong f70b177e56 Misc
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-29 21:30:05 +08:00
Syrone Wong e7c00dd8e7 Merge pull request #682 from Noisyfox/dev
New approach to record and calculate inbound/outbound speed.
2016-08-27 10:33:04 +08:00
noisyfox 5b2327776a Fix a null ref exception. 2016-08-27 03:35:33 +10:00
noisyfox ed54b249db Close services while closing listener.
Now it will close all existing TCP relay connections
while reloading the listener.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-26 21:08:04 +10:00
noisyfox 65d72fdfed New approach to record and calculate inbound/outbound speed.
Use lock to sync between threads.This should fix issue #679
and other same issue.

Fix a bug in UpdateSpeed() that only save the first xxxSpeedRecord.
2016-08-26 13:49:44 +10:00
noisyfox a7b0ce5bcb Use atomic long read.
Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-26 12:23:21 +10:00
Syrone Wong f935514c05 Merge pull request #667 from Noisyfox/dev
Minor bug fix and improvements.
2016-08-22 15:10:05 +08:00
noisyfox 37d894594e Use immutable remote reference during async process.
To prevent "The IAsyncResult object was not returned from
the corresponding asynchronous method on this class." exception.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-22 11:49:53 +10:00
noisyfox 28fe2fdc54 Only output some debug exceptions in debug mode.
Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-22 10:53:18 +10:00
noisyfox f676d2185e Use Socket.ConnectAsync instead of socket.BeginConnect.
Socket.ConnectAsync will create socket and handle the address family
itself. This solves some compatibility issues on win7.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-20 11:36:27 +10:00
noisyfox b335e6c8f5 Revert changes in UDPRelay.cs
UDP SendTo doesn't support DnsEndPoint.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-20 11:33:59 +10:00
noisyfox 80fc8b4ccf Fix an exception caused by process.MainModule.
Try to access a dead process's MainModule will cause
"A 32 bit processes cannot access modules of
a 64 bit process." exception on win7 x64.
Simply ignore it.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-20 10:57:13 +10:00
noisyfox 70624e35d9 Fix compatibility issue on vista and win7.
Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-19 13:56:55 +10:00
noisyfox 2ed8ab50cd Only parse socks5 header once per connection.
To avoid unreadable and uncessary output

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-19 13:36:49 +10:00
Noisyfox a1eef6eac1 Multiple instances of ss_privoxy (#665)
* Use job object to manage polipo service's external processes.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>

* Only kill those ss_privoxy.exe created by this ss instance.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>

* Update README

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-18 19:31:01 +08:00
Syrone Wong e30487207b Add error message
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-17 17:02:30 +08:00
Syrone Wong 8637277c27 Cleanup
- drop unused using directive
- add a comment

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-17 15:58:00 +08:00
Syrone Wong 2b4b6cba07 Minor ConfigForm changes
- add event handler for KeyDown
- drop unused code, we don't support
  table and rc4 now

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-17 15:56:00 +08:00
Syrone Wong acf7963de7 OTA is stable now
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-17 15:52:42 +08:00
Syrone Wong 64e21e031d Merge PR #660 from @Noisyfox
Resolving domain name through socks5 proxy
and
dual-mode socket
2016-08-17 14:12:38 +08:00
Syrone Wong ddb7374432 Refine Windows registry again(#512)
Used a method added in .NET 4, user may not have
this version installed, reverting to an older
method seems to be a better solution.

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-17 11:13:35 +08:00
Syrone Wong 3417a984fa Change requirements
- Running on Windows Vista or above

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-17 11:13:25 +08:00
noisyfox 6cb58b229d Fix a null ref exception.
At that time the remote even doesn't exist.
Also this debug output doesn't make any sense because when handling
udp request, this tcp connection won't have any meaningful data.
So I simply removed these two lines.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-16 20:52:53 +10:00
noisyfox 53ce1948e4 Unified ipv4, ipv6 and domain connections.
-Use dual-mode socket & DnsEndPoint to handle all domain name connection.
-Move all socket creation into one single function.

So that we could treat domain name just like ipv4 & ipv6 address.
This also solves the async domain solving problem because the os will
hanlde it for us.
2016-08-16 20:40:09 +10:00
noisyfox ad15c7e64b Improve host address handling using DnsEndPoint
Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-16 17:28:26 +10:00
noisyfox aef88543d2 Fix typo
Update translate

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-16 15:47:55 +10:00
noisyfox 22740c5102 Let Socks5 proxy handle server host's domain name.
Don't resolve it locally because socks5 support domain name connection.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-16 02:10:38 +10:00
noisyfox a02cd11dc9 Let proxy module handle name resolving itself.
Fix wrong output in proxyConnectTimer_Elapsed.

Signed-off-by: noisyfox <timemanager.rick@gmail.com>
2016-08-16 01:11:33 +10:00
Syrone Wong 21618ef08d Cleanup
- Drop redundant code.
  I should read code carefully, mbed TLS will set operation
  when invoking mbedtls_cipher_setkey(), the last param
  const mbedtls_operation_t operation is what we want.
- minor changes

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-14 22:29:48 +08:00
Syrone Wong e15e2cf700 Update CHANGES
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-08-14 22:28:50 +08:00
46 changed files with 1171 additions and 413 deletions
+15 -1
View File
@@ -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
+2 -5
View File
@@ -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
+26 -13
View File
@@ -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 -12
View File
@@ -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)
+142 -73
View File
@@ -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);
}
+4 -2
View File
@@ -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.
+118
View File
@@ -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
{
+61 -48
View File
@@ -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);
}
+1 -4
View File
@@ -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
+4 -4
View File
@@ -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[]>> {
+1 -1
View File
@@ -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);
+8
View File
@@ -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
View File
@@ -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>
+16 -14
View File
@@ -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,
+1 -1
View File
@@ -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);
+46 -36
View File
@@ -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();
}
}
}
+132
View File
@@ -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;
}
}
}
+57 -4
View File
@@ -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
View File
@@ -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);
+5 -16
View File
@@ -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
View File
@@ -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";
+76 -44
View File
@@ -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);
-1
View File
@@ -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
View File
@@ -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>