Browse Source

Merge branch 'master' into feature/statistics_ui

tags/3.0
icylogic 8 years ago
parent
commit
5b895efdba
48 changed files with 1022 additions and 2532 deletions
  1. +1
    -0
      .gitignore
  2. +1
    -0
      nuget.config
  3. +0
    -1883
      shadowsocks-csharp/3rd/SimpleJson.cs
  4. +4
    -12
      shadowsocks-csharp/Controller/FileManager.cs
  5. +55
    -12
      shadowsocks-csharp/Controller/Logging.cs
  6. +45
    -38
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  7. +24
    -17
      shadowsocks-csharp/Controller/Service/GfwListUpdater.cs
  8. +7
    -9
      shadowsocks-csharp/Controller/Service/Listener.cs
  9. +93
    -40
      shadowsocks-csharp/Controller/Service/PACServer.cs
  10. +14
    -20
      shadowsocks-csharp/Controller/Service/PolipoRunner.cs
  11. +0
    -3
      shadowsocks-csharp/Controller/Service/PortForwarder.cs
  12. +92
    -81
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  13. +17
    -8
      shadowsocks-csharp/Controller/Service/UDPRelay.cs
  14. +56
    -25
      shadowsocks-csharp/Controller/Service/UpdateChecker.cs
  15. +68
    -9
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  16. +5
    -5
      shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs
  17. +2
    -1
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
  18. +37
    -7
      shadowsocks-csharp/Controller/System/AutoStartup.cs
  19. BIN
      shadowsocks-csharp/Data/libsscrypto.dll.gz
  20. +3
    -3
      shadowsocks-csharp/Data/privoxy_conf.txt
  21. +1
    -1
      shadowsocks-csharp/Encryption/EncryptorBase.cs
  22. +1
    -5
      shadowsocks-csharp/Encryption/EncryptorFactory.cs
  23. +16
    -13
      shadowsocks-csharp/Encryption/IVEncryptor.cs
  24. +65
    -0
      shadowsocks-csharp/Encryption/MbedTLS.cs
  25. +7
    -10
      shadowsocks-csharp/Encryption/PolarSSL.cs
  26. +1
    -2
      shadowsocks-csharp/Encryption/PolarSSLEncryptor.cs
  27. +10
    -10
      shadowsocks-csharp/Encryption/Sodium.cs
  28. +5
    -0
      shadowsocks-csharp/Encryption/SodiumEncryptor.cs
  29. +0
    -106
      shadowsocks-csharp/Encryption/TableEncryptor.cs
  30. +11
    -57
      shadowsocks-csharp/Model/Configuration.cs
  31. +31
    -8
      shadowsocks-csharp/Model/LogViewerConfig.cs
  32. +15
    -19
      shadowsocks-csharp/Model/Server.cs
  33. +7
    -8
      shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs
  34. +14
    -10
      shadowsocks-csharp/Program.cs
  35. +56
    -13
      shadowsocks-csharp/Util/Util.cs
  36. +2
    -3
      shadowsocks-csharp/View/ConfigForm.Designer.cs
  37. +7
    -4
      shadowsocks-csharp/View/ConfigForm.cs
  38. +12
    -11
      shadowsocks-csharp/View/LogForm.Designer.cs
  39. +35
    -19
      shadowsocks-csharp/View/LogForm.cs
  40. +24
    -0
      shadowsocks-csharp/View/LogForm.resx
  41. +77
    -18
      shadowsocks-csharp/View/MenuViewController.cs
  42. +9
    -8
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs
  43. +2
    -2
      shadowsocks-csharp/app.config
  44. +10
    -8
      shadowsocks-csharp/packages.config
  45. +22
    -24
      shadowsocks-csharp/shadowsocks-csharp.csproj
  46. +15
    -0
      test/UnitTest.cs
  47. +8
    -0
      test/packages.config
  48. +35
    -0
      test/test.csproj

+ 1
- 0
.gitignore View File

@@ -1,3 +1,4 @@
/.vs/
Backup/ Backup/
bin/ bin/
obj/ obj/


+ 1
- 0
nuget.config View File

@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<config> <config>
<add key="repositoryPath" value="shadowsocks-csharp\3rd" /> <add key="repositoryPath" value="shadowsocks-csharp\3rd" />


+ 0
- 1883
shadowsocks-csharp/3rd/SimpleJson.cs
File diff suppressed because it is too large
View File


+ 4
- 12
shadowsocks-csharp/Controller/FileManager.cs View File

@@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Text;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -12,9 +10,7 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
System.IO.FileStream _FileStream =
new System.IO.FileStream(fileName, System.IO.FileMode.Create,
System.IO.FileAccess.Write);
FileStream _FileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write);
_FileStream.Write(content, 0, content.Length); _FileStream.Write(content, 0, content.Length);
_FileStream.Close(); _FileStream.Close();
return true; return true;
@@ -31,7 +27,7 @@ namespace Shadowsocks.Controller
{ {
FileStream destinationFile = File.Create(fileName); FileStream destinationFile = File.Create(fileName);
// Because the uncompressed size of the file is unknown,
// Because the uncompressed size of the file is unknown,
// we are using an arbitrary buffer size. // we are using an arbitrary buffer size.
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int n; int n;
@@ -39,17 +35,13 @@ namespace Shadowsocks.Controller
using (GZipStream input = new GZipStream(new MemoryStream(content), using (GZipStream input = new GZipStream(new MemoryStream(content),
CompressionMode.Decompress, false)) CompressionMode.Decompress, false))
{ {
while (true)
while ((n = input.Read(buffer, 0, buffer.Length)) > 0)
{ {
n = input.Read(buffer, 0, buffer.Length);
if (n == 0)
{
break;
}
destinationFile.Write(buffer, 0, n); destinationFile.Write(buffer, 0, n);
} }
} }
destinationFile.Close(); destinationFile.Close();
} }
} }
} }

+ 55
- 12
shadowsocks-csharp/Controller/Logging.cs View File

@@ -1,23 +1,23 @@
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System;
using System.IO; using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text;
using System.Net;
using Shadowsocks.Util;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
public class Logging public class Logging
{ {
public static string LogFile;
public static string LogFilePath;
public static bool OpenLogFile() public static bool OpenLogFile()
{ {
try try
{ {
string temppath = Utils.GetTempPath();
LogFile = Path.Combine(temppath, "shadowsocks.log");
FileStream fs = new FileStream(LogFile, FileMode.Append);
LogFilePath = Utils.GetTempPath("shadowsocks.log");
FileStream fs = new FileStream(LogFilePath, FileMode.Append);
StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs);
sw.AutoFlush = true; sw.AutoFlush = true;
Console.SetOut(sw); Console.SetOut(sw);
@@ -32,11 +32,46 @@ namespace Shadowsocks.Controller
} }
} }
private static void WriteToLogFile(object o)
{
Console.WriteLine(o);
}
public static void Error(object o)
{
WriteToLogFile("[E] " + o);
}
public static void Info(object o)
{
WriteToLogFile(o);
}
public static void Debug(object o) public static void Debug(object o)
{ {
#if DEBUG
WriteToLogFile("[D] " + o);
#endif
}
public static void Debug(EndPoint local, EndPoint remote, int len, string header = null, string tailer = null)
{
#if DEBUG #if DEBUG
Console.WriteLine(o);
if (header == null && tailer == null)
Debug($"{local} => {remote} (size={len})");
else if (header == null && tailer != null)
Debug($"{local} => {remote} (size={len}), {tailer}");
else if (header != null && tailer == null)
Debug($"{header}: {local} => {remote} (size={len})");
else
Debug($"{header}: {local} => {remote} (size={len}), {tailer}");
#endif
}
public static void Debug(Socket sock, int len, string header = null, string tailer = null)
{
#if DEBUG
Debug(sock.LocalEndPoint, sock.RemoteEndPoint, len, header, tailer);
#endif #endif
} }
@@ -57,11 +92,19 @@ namespace Shadowsocks.Controller
} }
else if (se.SocketErrorCode == SocketError.NotConnected) else if (se.SocketErrorCode == SocketError.NotConnected)
{ {
// close when not connected
// The application tried to send or receive data, and the System.Net.Sockets.Socket is not connected.
}
else if (se.SocketErrorCode == SocketError.HostUnreachable)
{
// There is no network route to the specified host.
}
else if (se.SocketErrorCode == SocketError.TimedOut)
{
// The connection attempt timed out, or the connected host has failed to respond.
} }
else else
{ {
Console.WriteLine(e);
Info(e);
} }
} }
else if (e is ObjectDisposedException) else if (e is ObjectDisposedException)
@@ -69,7 +112,7 @@ namespace Shadowsocks.Controller
} }
else else
{ {
Console.WriteLine(e);
Info(e);
} }
} }
} }


+ 45
- 38
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -9,6 +9,10 @@ using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using Shadowsocks.Model; using Shadowsocks.Model;
using Shadowsocks.Util; using Shadowsocks.Util;


@@ -30,8 +34,8 @@ namespace Shadowsocks.Controller
public Statistics FilteredStatistics { get; private set; } public Statistics FilteredStatistics { get; private set; }
public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1);
private int Repeat => _config.RepeatTimesNum; private int Repeat => _config.RepeatTimesNum;
private const int RetryInterval = 2*60*1000; //retry 2 minutes after failed
private int Interval => (int) TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds;
private const int RetryInterval = 2 * 60 * 1000; //retry 2 minutes after failed
private int Interval => (int)TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds;
private Timer _timer; private Timer _timer;
private State _state; private State _state;
private List<Server> _servers; private List<Server> _servers;
@@ -42,8 +46,7 @@ namespace Shadowsocks.Controller
//static constructor to initialize every public static fields before refereced //static constructor to initialize every public static fields before refereced
static AvailabilityStatistics() static AvailabilityStatistics()
{ {
var temppath = Utils.GetTempPath();
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName);
AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName);
} }


public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig) public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig)
@@ -107,11 +110,18 @@ namespace Shadowsocks.Controller
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
return null; return null;
} }
dynamic obj;
if (!SimpleJson.SimpleJson.TryDeserializeObject(jsonString, out obj)) return null;
string country = obj["country"];
string city = obj["city"];
string isp = obj["isp"];
JObject obj;
try
{
obj = JObject.Parse(jsonString);
}
catch (JsonReaderException)
{
return null;
}
string country = (string)obj["country"];
string city = (string)obj["city"];
string isp = (string)obj["isp"];
if (country == null || city == null || isp == null) return null; if (country == null || city == null || isp == null) return null;
return new DataList { return new DataList {
new DataUnit(State.Geolocation, $"\"{country} {city}\""), new DataUnit(State.Geolocation, $"\"{country} {city}\""),
@@ -123,11 +133,10 @@ namespace Shadowsocks.Controller
{ {
Logging.Debug("Ping " + server.FriendlyName()); Logging.Debug("Ping " + server.FriendlyName());
if (server.server == "") return null; if (server.server == "") return null;
var IP = Dns.GetHostAddresses(server.server).First(ip => ip.AddressFamily == AddressFamily.InterNetwork);
var IP = Dns.GetHostAddresses(server.server).First(ip => (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6));
var ping = new Ping(); var ping = new Ping();
var ret = new List<DataList>(); var ret = new List<DataList>();
foreach (
var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString(DateTimePattern)))
foreach (var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString(DateTimePattern)))
{ {
//ICMP echo. we can also set options and special bytes //ICMP echo. we can also set options and special bytes
try try
@@ -146,7 +155,7 @@ namespace Shadowsocks.Controller
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine($"An exception occured when eveluating {server.FriendlyName()}");
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}");
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
} }
} }
@@ -182,11 +191,11 @@ namespace Shadowsocks.Controller
if (!File.Exists(AvailabilityStatisticsFile)) if (!File.Exists(AvailabilityStatisticsFile))
{ {
var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray());
lines = new[] {headerLine, dataLine};
lines = new[] { headerLine, dataLine };
} }
else else
{ {
lines = new[] {dataLine};
lines = new[] { dataLine };
} }
try try
{ {
@@ -249,30 +258,29 @@ namespace Shadowsocks.Controller
Logging.Debug($"loading statistics from {path}"); Logging.Debug($"loading statistics from {path}");
if (!File.Exists(path)) if (!File.Exists(path))
{ {
Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval/60/1000} minutes later");
Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval / 60 / 1000} minutes later");
_timer.Change(RetryInterval, Interval); _timer.Change(RetryInterval, Interval);
return; return;
} }
RawStatistics = (from l in File.ReadAllLines(path)
.Skip(1)
let strings = l.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
let rawData = new RawStatisticsData
{
Timestamp = ParseExactOrUnknown(strings[0]),
ServerName = strings[1],
ICMPStatus = strings[2],
RoundtripTime = int.Parse(strings[3]),
Geolocation = 5 > strings.Length ?
null
: strings[4],
ISP = 6 > strings.Length ? null : strings[5]
}
group rawData by rawData.ServerName into server
select new
{
ServerName = server.Key,
data = server.ToList()
}).ToDictionary(server => server.ServerName, server=> server.data);
RawStatistics = (from l in File.ReadAllLines(path).Skip(1)
let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
let rawData = new RawStatisticsData
{
Timestamp = ParseExactOrUnknown(strings[0]),
ServerName = strings[1],
ICMPStatus = strings[2],
RoundtripTime = int.Parse(strings[3]),
Geolocation = 5 > strings.Length ?
null
: strings[4],
ISP = 6 > strings.Length ? null : strings[5]
}
group rawData by rawData.ServerName into server
select new
{
ServerName = server.Key,
data = server.ToList()
}).ToDictionary(server => server.ServerName, server => server.data);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -301,7 +309,7 @@ namespace Shadowsocks.Controller
public string ICMPStatus; public string ICMPStatus;
public int RoundtripTime; public int RoundtripTime;
public string Geolocation; public string Geolocation;
public string ISP ;
public string ISP;
} }


public class StatisticsData public class StatisticsData
@@ -311,6 +319,5 @@ namespace Shadowsocks.Controller
public int MinResponse; public int MinResponse;
public int MaxResponse; public int MaxResponse;
} }

} }
} }

+ 24
- 17
shadowsocks-csharp/Controller/Service/GfwListUpdater.cs View File

@@ -1,12 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO; using System.IO;
using System.Net;
using System.Text;
using Newtonsoft.Json;
using Shadowsocks.Model;
using Shadowsocks.Properties; using Shadowsocks.Properties;
using SimpleJson;
using Shadowsocks.Util; using Shadowsocks.Util;
using Shadowsocks.Model;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -14,10 +16,6 @@ namespace Shadowsocks.Controller
{ {
private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"; private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt";
private static string PAC_FILE = PACServer.PAC_FILE;
private static string USER_RULE_FILE = PACServer.USER_RULE_FILE;
public event EventHandler<ResultEventArgs> UpdateCompleted; public event EventHandler<ResultEventArgs> UpdateCompleted;
public event ErrorEventHandler Error; public event ErrorEventHandler Error;
@@ -36,30 +34,39 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
File.WriteAllText(Utils.GetTempPath("gfwlist.txt"), e.Result, Encoding.UTF8);
List<string> lines = ParseResult(e.Result); List<string> lines = ParseResult(e.Result);
if (File.Exists(USER_RULE_FILE))
if (File.Exists(PACServer.USER_RULE_FILE))
{ {
string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8);
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach(string rule in rules)
foreach (string rule in rules)
{ {
if (rule.StartsWith("!") || rule.StartsWith("[")) if (rule.StartsWith("!") || rule.StartsWith("["))
continue; continue;
lines.Add(rule); lines.Add(rule);
} }
} }
string abpContent = Utils.UnGzip(Resources.abp_js);
abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines));
if (File.Exists(PAC_FILE))
string abpContent;
if (File.Exists(PACServer.USER_ABP_FILE))
{
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
}
else
{
abpContent = Utils.UnGzip(Resources.abp_js);
}
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
if (File.Exists(PACServer.PAC_FILE))
{ {
string original = File.ReadAllText(PAC_FILE, Encoding.UTF8);
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
if (original == abpContent) if (original == abpContent)
{ {
UpdateCompleted(this, new ResultEventArgs(false)); UpdateCompleted(this, new ResultEventArgs(false));
return; return;
} }
} }
File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8);
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8);
if (UpdateCompleted != null) if (UpdateCompleted != null)
{ {
UpdateCompleted(this, new ResultEventArgs(true)); UpdateCompleted(this, new ResultEventArgs(true));
@@ -82,7 +89,7 @@ namespace Shadowsocks.Controller
http.DownloadStringAsync(new Uri(GFWLIST_URL)); http.DownloadStringAsync(new Uri(GFWLIST_URL));
} }
public List<string> ParseResult(string response)
public static List<string> ParseResult(string response)
{ {
byte[] bytes = Convert.FromBase64String(response); byte[] bytes = Convert.FromBase64String(response);
string content = Encoding.ASCII.GetString(bytes); string content = Encoding.ASCII.GetString(bytes);


+ 7
- 9
shadowsocks-csharp/Controller/Service/Listener.cs View File

@@ -1,10 +1,10 @@
using Shadowsocks.Model;
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text;
using Shadowsocks.Model;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -78,10 +78,8 @@ namespace Shadowsocks.Controller
_tcpSocket.Listen(1024); _tcpSocket.Listen(1024);
// Start an asynchronous socket to listen for connections. // Start an asynchronous socket to listen for connections.
Console.WriteLine("Shadowsocks started");
_tcpSocket.BeginAccept(
new AsyncCallback(AcceptCallback),
_tcpSocket);
Logging.Info("Shadowsocks started");
_tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket);
UDPState udpState = new UDPState(); UDPState udpState = new UDPState();
_udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState);
} }
@@ -163,7 +161,7 @@ namespace Shadowsocks.Controller
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e);
Logging.LogUsefulException(e);
} }
finally finally
{ {
@@ -208,7 +206,7 @@ namespace Shadowsocks.Controller
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e);
Logging.LogUsefulException(e);
conn.Close(); conn.Close();
} }
} }


+ 93
- 40
shadowsocks-csharp/Controller/Service/PACServer.cs View File

@@ -1,31 +1,33 @@
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System;
using System.Collections;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
class PACServer : Listener.Service class PACServer : Listener.Service
{ {
public static string PAC_FILE = "pac.txt";
public static string USER_RULE_FILE = "user-rule.txt";
public static readonly string PAC_FILE = "pac.txt";
public static readonly string USER_RULE_FILE = "user-rule.txt";
public static readonly string USER_ABP_FILE = "abp.txt";
FileSystemWatcher watcher;
FileSystemWatcher PACFileWatcher;
FileSystemWatcher UserRuleFileWatcher;
private Configuration _config; private Configuration _config;
public event EventHandler PACFileChanged; public event EventHandler PACFileChanged;
public event EventHandler UserRuleFileChanged;
public PACServer() public PACServer()
{ {
this.WatchPacFile(); this.WatchPacFile();
this.WatchUserRuleFile();
} }
public void UpdateConfiguration(Configuration config) public void UpdateConfiguration(Configuration config)
@@ -46,7 +48,7 @@ namespace Shadowsocks.Controller
bool hostMatch = false, pathMatch = false, useSocks = false; bool hostMatch = false, pathMatch = false, useSocks = false;
foreach (string line in lines) foreach (string line in lines)
{ {
string[] kv = line.Split(new char[]{':'}, 2);
string[] kv = line.Split(new char[] { ':' }, 2);
if (kv.Length == 2) if (kv.Length == 2)
{ {
if (kv[0] == "Host") if (kv[0] == "Host")
@@ -56,14 +58,14 @@ namespace Shadowsocks.Controller
hostMatch = true; hostMatch = true;
} }
} }
else if (kv[0] == "User-Agent")
{
// we need to drop connections when changing servers
/* if (kv[1].IndexOf("Chrome") >= 0)
{
useSocks = true;
} */
}
//else if (kv[0] == "User-Agent")
//{
// // we need to drop connections when changing servers
// if (kv[1].IndexOf("Chrome") >= 0)
// {
// useSocks = true;
// }
//}
} }
else if (kv.Length == 1) else if (kv.Length == 1)
{ {
@@ -142,14 +144,14 @@ Content-Type: application/x-ns-proxy-autoconfig
Content-Length: {0} Content-Length: {0}
Connection: Close Connection: Close
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
", Encoding.UTF8.GetBytes(pac).Length) + pac;
byte[] response = Encoding.UTF8.GetBytes(text);
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket);
Util.Utils.ReleaseMemory(true);
Utils.ReleaseMemory(true);
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e);
Logging.LogUsefulException(e);
socket.Close(); socket.Close();
} }
} }
@@ -167,27 +169,78 @@ Connection: Close
private void WatchPacFile() private void WatchPacFile()
{ {
if (watcher != null)
{
watcher.Dispose();
}
watcher = new FileSystemWatcher(Directory.GetCurrentDirectory());
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = PAC_FILE;
watcher.Changed += Watcher_Changed;
watcher.Created += Watcher_Changed;
watcher.Deleted += Watcher_Changed;
watcher.Renamed += Watcher_Changed;
watcher.EnableRaisingEvents = true;
if (PACFileWatcher != null)
{
PACFileWatcher.Dispose();
}
PACFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory());
PACFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
PACFileWatcher.Filter = PAC_FILE;
PACFileWatcher.Changed += PACFileWatcher_Changed;
PACFileWatcher.Created += PACFileWatcher_Changed;
PACFileWatcher.Deleted += PACFileWatcher_Changed;
PACFileWatcher.Renamed += PACFileWatcher_Changed;
PACFileWatcher.EnableRaisingEvents = true;
} }
private void Watcher_Changed(object sender, FileSystemEventArgs e)
private void WatchUserRuleFile()
{ {
if (PACFileChanged != null)
if (UserRuleFileWatcher != null)
{ {
PACFileChanged(this, new EventArgs());
UserRuleFileWatcher.Dispose();
}
UserRuleFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory());
UserRuleFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
UserRuleFileWatcher.Filter = USER_RULE_FILE;
UserRuleFileWatcher.Changed += UserRuleFileWatcher_Changed;
UserRuleFileWatcher.Created += UserRuleFileWatcher_Changed;
UserRuleFileWatcher.Deleted += UserRuleFileWatcher_Changed;
UserRuleFileWatcher.Renamed += UserRuleFileWatcher_Changed;
UserRuleFileWatcher.EnableRaisingEvents = true;
}
#region FileSystemWatcher.OnChanged()
// FileSystemWatcher Changed event is raised twice
// http://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice
private static Hashtable fileChangedTime = new Hashtable();
private void PACFileWatcher_Changed(object sender, FileSystemEventArgs e)
{
string path = e.FullPath.ToString();
string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString();
// 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)
{
if (PACFileChanged != null)
{
Logging.Info($"Detected: PAC file '{e.Name}' was {e.ChangeType.ToString().ToLower()}.");
PACFileChanged(this, new EventArgs());
}
// lastly we update the last write time in the hashtable
fileChangedTime[path] = currentLastWriteTime;
}
}
private void UserRuleFileWatcher_Changed(object sender, FileSystemEventArgs e)
{
string path = e.FullPath.ToString();
string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString();
// 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)
{
if (UserRuleFileChanged != null)
{
Logging.Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}.");
UserRuleFileChanged(this, new EventArgs());
}
// lastly we update the last write time in the hashtable
fileChangedTime[path] = currentLastWriteTime;
} }
} }
#endregion
private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks) private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks)
{ {
@@ -202,7 +255,7 @@ Connection: Close
//} //}
//catch (Exception e) //catch (Exception e)
//{ //{
// Console.WriteLine(e);
// Logging.LogUsefulException(e);
//} //}
return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";"; return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";";
} }


+ 14
- 20
shadowsocks-csharp/Controller/Service/PolipoRunner.cs View File

@@ -1,14 +1,14 @@
using Shadowsocks.Model;
using Shadowsocks.Properties;
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression;
using System.Text;
using System.Net.NetworkInformation;
using System.Net; using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util; using Shadowsocks.Util;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
@@ -16,16 +16,14 @@ namespace Shadowsocks.Controller
class PolipoRunner class PolipoRunner
{ {
private Process _process; private Process _process;
private static string temppath;
private int _runningPort; private int _runningPort;
static PolipoRunner() static PolipoRunner()
{ {
temppath = Utils.GetTempPath();
try try
{ {
FileManager.UncompressFile(temppath + "/ss_privoxy.exe", Resources.privoxy_exe);
FileManager.UncompressFile(temppath + "/mgwz.dll", Resources.mgwz_dll);
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe);
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll);
} }
catch (IOException e) catch (IOException e)
{ {
@@ -56,7 +54,7 @@ namespace Shadowsocks.Controller
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e.ToString());
Logging.LogUsefulException(e);
} }
} }
string polipoConfig = Resources.privoxy_conf; string polipoConfig = Resources.privoxy_conf;
@@ -64,20 +62,16 @@ namespace Shadowsocks.Controller
polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.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"); polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig));
FileManager.ByteArrayToFile(Utils.GetTempPath("privoxy.conf"), Encoding.UTF8.GetBytes(polipoConfig));
if (!(temppath.EndsWith("\\") || temppath.EndsWith("/"))) {
temppath = temppath + "\\";
}
_process = new Process(); _process = new Process();
// Configure the process using the StartInfo properties. // Configure the process using the StartInfo properties.
_process.StartInfo.FileName = temppath + "ss_privoxy.exe";
_process.StartInfo.Arguments = " \"" + temppath + "privoxy.conf\"";
_process.StartInfo.FileName = "ss_privoxy.exe";
_process.StartInfo.Arguments = "privoxy.conf";
_process.StartInfo.WorkingDirectory = Utils.GetTempPath();
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; _process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_process.StartInfo.UseShellExecute = true; _process.StartInfo.UseShellExecute = true;
_process.StartInfo.CreateNoWindow = true; _process.StartInfo.CreateNoWindow = true;
//_process.StartInfo.RedirectStandardOutput = true;
//_process.StartInfo.RedirectStandardError = true;
_process.Start(); _process.Start();
} }
RefreshTrayArea(); RefreshTrayArea();
@@ -94,7 +88,7 @@ namespace Shadowsocks.Controller
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e.ToString());
Logging.LogUsefulException(e);
} }
_process = null; _process = null;
} }


+ 0
- 3
shadowsocks-csharp/Controller/Service/PortForwarder.cs View File

@@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -140,7 +138,6 @@ namespace Shadowsocks.Controller
} }
else else
{ {
//Console.WriteLine("bytesRead: " + bytesRead.ToString());
_local.Shutdown(SocketShutdown.Send); _local.Shutdown(SocketShutdown.Send);
_localShutdown = true; _localShutdown = true;
CheckClose(); CheckClose();


+ 92
- 81
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -1,31 +1,30 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Timers;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption; using Shadowsocks.Encryption;
using Shadowsocks.Model; using Shadowsocks.Model;
using Shadowsocks.Controller.Strategy;
using System.Timers;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
class TCPRelay : Listener.Service class TCPRelay : Listener.Service
{ {
private ShadowsocksController _controller; private ShadowsocksController _controller;
private DateTime _lastSweepTime; private DateTime _lastSweepTime;
public ISet<Handler> Handlers
public ISet<TCPHandler> Handlers
{ {
get; set; get; set;
} }
public TCPRelay(ShadowsocksController controller) public TCPRelay(ShadowsocksController controller)
{ {
this._controller = controller;
this.Handlers = new HashSet<Handler>();
this._lastSweepTime = DateTime.Now;
_controller = controller;
Handlers = new HashSet<TCPHandler>();
_lastSweepTime = DateTime.Now;
} }
public bool Handle(byte[] firstPacket, int length, Socket socket, object state) public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
@@ -39,22 +38,21 @@ namespace Shadowsocks.Controller
return false; return false;
} }
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
Handler handler = new Handler();
TCPHandler handler = new TCPHandler(this);
handler.connection = socket; handler.connection = socket;
handler.controller = _controller; handler.controller = _controller;
handler.relay = this; handler.relay = this;
handler.Start(firstPacket, length); handler.Start(firstPacket, length);
IList<Handler> handlersToClose = new List<Handler>();
lock (this.Handlers)
IList<TCPHandler> handlersToClose = new List<TCPHandler>();
lock (Handlers)
{ {
this.Handlers.Add(handler);
Logging.Debug($"connections: {Handlers.Count}");
Handlers.Add(handler);
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) if (now - _lastSweepTime > TimeSpan.FromSeconds(1))
{ {
_lastSweepTime = now; _lastSweepTime = now;
foreach (Handler handler1 in this.Handlers)
foreach (TCPHandler handler1 in Handlers)
{ {
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) if (now - handler1.lastActivity > TimeSpan.FromSeconds(900))
{ {
@@ -63,18 +61,28 @@ namespace Shadowsocks.Controller
} }
} }
} }
foreach (Handler handler1 in handlersToClose)
foreach (TCPHandler handler1 in handlersToClose)
{ {
Logging.Debug("Closing timed out connection");
Logging.Debug("Closing timed out TCP connection.");
handler1.Close(); handler1.Close();
} }
return true;
return true;
}
public void UpdateInboundCounter(long n)
{
_controller.UpdateInboundCounter(n);
}
public void UpdateOutboundCounter(long n)
{
_controller.UpdateOutboundCounter(n);
} }
} }
class Handler
class TCPHandler
{ {
//public Encryptor encryptor;
// public Encryptor encryptor;
public IEncryptor encryptor; public IEncryptor encryptor;
public Server server; public Server server;
// Client socket. // Client socket.
@@ -85,6 +93,7 @@ namespace Shadowsocks.Controller
public DateTime lastActivity; public DateTime lastActivity;
private const int maxRetry = 4;
private int retryCount = 0; private int retryCount = 0;
private bool connected; private bool connected;
@@ -117,6 +126,12 @@ namespace Shadowsocks.Controller
private object decryptionLock = new object(); private object decryptionLock = new object();
private DateTime _startConnectTime; private DateTime _startConnectTime;
private TCPRelay tcprelay; // TODO: tcprelay ?= relay
public TCPHandler(TCPRelay tcprelay)
{
this.tcprelay = tcprelay;
}
public void CreateRemote() public void CreateRemote()
{ {
@@ -125,23 +140,23 @@ namespace Shadowsocks.Controller
{ {
throw new ArgumentException("No server configured"); throw new ArgumentException("No server configured");
} }
this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.one_time_auth, false);
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
this.server = server; this.server = server;
} }
public void Start(byte[] firstPacket, int length) public void Start(byte[] firstPacket, int length)
{ {
this._firstPacket = firstPacket;
this._firstPacketLength = length;
this.HandshakeReceive();
this.lastActivity = DateTime.Now;
_firstPacket = firstPacket;
_firstPacketLength = length;
HandshakeReceive();
lastActivity = DateTime.Now;
} }
private void CheckClose() private void CheckClose()
{ {
if (connectionShutdown && remoteShutdown) if (connectionShutdown && remoteShutdown)
{ {
this.Close();
Close();
} }
} }
@@ -149,7 +164,6 @@ namespace Shadowsocks.Controller
{ {
lock (relay.Handlers) lock (relay.Handlers)
{ {
Logging.Debug($"connections: {relay.Handlers.Count}");
relay.Handlers.Remove(this); relay.Handlers.Remove(this);
} }
lock (this) lock (this)
@@ -213,19 +227,19 @@ namespace Shadowsocks.Controller
{ {
// reject socks 4 // reject socks 4
response = new byte[] { 0, 91 }; response = new byte[] { 0, 91 };
Console.WriteLine("socks 5 protocol error");
Logging.Error("socks 5 protocol error");
} }
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null);
} }
else else
{ {
this.Close();
Close();
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -239,20 +253,19 @@ namespace Shadowsocks.Controller
{ {
connection.EndSend(ar); connection.EndSend(ar);
// +----+-----+-------+------+----------+----------+
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +----+-----+-------+------+----------+----------+
// +-----+-----+-------+------+----------+----------+
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +-----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +-----+-----+-------+------+----------+----------+
// Skip first 3 bytes // Skip first 3 bytes
// TODO validate // TODO validate
connection.BeginReceive(connetionRecvBuffer, 0, 3, 0,
new AsyncCallback(handshakeReceive2Callback), null);
connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null);
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -281,14 +294,14 @@ namespace Shadowsocks.Controller
} }
else else
{ {
Console.WriteLine("failed to recv data in handshakeReceive2Callback");
this.Close();
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
Close();
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -324,27 +337,27 @@ namespace Shadowsocks.Controller
if (ar.AsyncState != null) if (ar.AsyncState != null)
{ {
connection.EndSend(ar); connection.EndSend(ar);
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(ReadAll), null);
Logging.Debug(remote, RecvSize, "TCP Relay");
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null);
} }
else else
{ {
int bytesRead = connection.EndReceive(ar); int bytesRead = connection.EndReceive(ar);
if (bytesRead > 0) if (bytesRead > 0)
{ {
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(ReadAll), null);
Logging.Debug(remote, RecvSize, "TCP Relay");
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null);
} }
else else
{ {
this.Close();
Close();
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -360,15 +373,16 @@ namespace Shadowsocks.Controller
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
// inner class
private class ServerTimer : Timer private class ServerTimer : Timer
{ {
public Server Server; public Server Server;
public ServerTimer(int p) :base(p)
public ServerTimer(int p) : base(p)
{ {
} }
} }
@@ -402,13 +416,12 @@ namespace Shadowsocks.Controller
connected = false; connected = false;
// Connect to the remote endpoint. // Connect to the remote endpoint.
remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), connectTimer);
remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer);
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -424,22 +437,22 @@ namespace Shadowsocks.Controller
{ {
strategy.SetFailure(server); strategy.SetFailure(server);
} }
Console.WriteLine(String.Format("{0} timed out", server.FriendlyName()));
Logging.Info($"{server.FriendlyName()} timed out");
remote.Close(); remote.Close();
RetryConnect(); RetryConnect();
} }
private void RetryConnect() private void RetryConnect()
{ {
if (retryCount < 4)
if (retryCount < maxRetry)
{ {
Logging.Debug("Connection failed, retrying");
Logging.Debug($"Connection failed, retry ({retryCount})");
StartConnect(); StartConnect();
retryCount++; retryCount++;
} }
else else
{ {
this.Close();
Close();
} }
} }
@@ -463,8 +476,7 @@ namespace Shadowsocks.Controller
connected = true; connected = true;
//Console.WriteLine("Socket connected to {0}",
// remote.RemoteEndPoint.ToString());
Logging.Debug($"Socket connected to {remote.RemoteEndPoint}");
var latency = DateTime.Now - _startConnectTime; var latency = DateTime.Now - _startConnectTime;
IStrategy strategy = controller.GetCurrentStrategy(); IStrategy strategy = controller.GetCurrentStrategy();
@@ -501,15 +513,13 @@ namespace Shadowsocks.Controller
} }
try try
{ {
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeConnectionReceiveCallback), null);
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null);
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null);
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -523,10 +533,11 @@ namespace Shadowsocks.Controller
{ {
int bytesRead = remote.EndReceive(ar); int bytesRead = remote.EndReceive(ar);
totalRead += bytesRead; totalRead += bytesRead;
tcprelay.UpdateInboundCounter(bytesRead);
if (bytesRead > 0) if (bytesRead > 0)
{ {
this.lastActivity = DateTime.Now;
lastActivity = DateTime.Now;
int bytesToSend; int bytesToSend;
lock (decryptionLock) lock (decryptionLock)
{ {
@@ -536,33 +547,33 @@ namespace Shadowsocks.Controller
} }
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend);
} }
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)");
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null);
IStrategy strategy = controller.GetCurrentStrategy(); IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null) if (strategy != null)
{ {
strategy.UpdateLastRead(this.server);
strategy.UpdateLastRead(server);
} }
} }
else else
{ {
//Console.WriteLine("bytesRead: " + bytesRead.ToString());
connection.Shutdown(SocketShutdown.Send); connection.Shutdown(SocketShutdown.Send);
connectionShutdown = true; connectionShutdown = true;
CheckClose(); CheckClose();
if (totalRead == 0)
{
// closed before anything received, reports as failure
// disable this feature
// controller.GetCurrentStrategy().SetFailure(this.server);
}
//if (totalRead == 0)
//{
// // closed before anything received, reports as failure
// // disable this feature
// controller.GetCurrentStrategy().SetFailure(this.server);
//}
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -588,12 +599,14 @@ namespace Shadowsocks.Controller
} }
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend);
} }
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)");
tcprelay.UpdateOutboundCounter(bytesToSend);
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null);
IStrategy strategy = controller.GetCurrentStrategy(); IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null) if (strategy != null)
{ {
strategy.UpdateLastWrite(this.server);
strategy.UpdateLastWrite(server);
} }
} }
else else
@@ -606,7 +619,7 @@ namespace Shadowsocks.Controller
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -619,13 +632,12 @@ namespace Shadowsocks.Controller
try try
{ {
remote.EndSend(ar); remote.EndSend(ar);
connection.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeConnectionReceiveCallback), null);
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null);
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
@@ -638,13 +650,12 @@ namespace Shadowsocks.Controller
try try
{ {
connection.EndSend(ar); connection.EndSend(ar);
remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null);
} }
catch (Exception e) catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
this.Close();
Close();
} }
} }
} }


+ 17
- 8
shadowsocks-csharp/Controller/Service/UDPRelay.cs View File

@@ -1,12 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
using System.Net.Sockets;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Shadowsocks.Controller.Strategy; using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -14,6 +14,10 @@ namespace Shadowsocks.Controller
{ {
private ShadowsocksController _controller; private ShadowsocksController _controller;
private LRUCache<IPEndPoint, UDPHandler> _cache; private LRUCache<IPEndPoint, UDPHandler> _cache;
public long outbound = 0;
public long inbound = 0;
public UDPRelay(ShadowsocksController controller) public UDPRelay(ShadowsocksController controller)
{ {
this._controller = controller; this._controller = controller;
@@ -70,23 +74,27 @@ namespace Shadowsocks.Controller
} }
_remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); _remoteEndPoint = new IPEndPoint(ipAddress, server.server_port);
_remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); _remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
} }
public void Send(byte[] data, int length) public void Send(byte[] data, int length)
{ {
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.one_time_auth, true);
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true);
byte[] dataIn = new byte[length - 3 + IVEncryptor.ONETIMEAUTH_BYTES]; byte[] dataIn = new byte[length - 3 + IVEncryptor.ONETIMEAUTH_BYTES];
Array.Copy(data, 3, dataIn, 0, length - 3); Array.Copy(data, 3, dataIn, 0, length - 3);
byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES]; byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES];
int outlen; int outlen;
encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen); encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen);
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
_remote.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); _remote.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint);
} }
public void Receive() public void Receive()
{ {
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
Logging.Debug($"++++++Receive Server Port, size:" + _buffer.Length);
_remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); _remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null);
} }
public void RecvFromCallback(IAsyncResult ar) public void RecvFromCallback(IAsyncResult ar)
{ {
try try
@@ -97,12 +105,13 @@ namespace Shadowsocks.Controller
byte[] dataOut = new byte[bytesRead]; byte[] dataOut = new byte[bytesRead];
int outlen; int outlen;
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.one_time_auth, true);
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true);
encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen); encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen);
byte[] sendBuf = new byte[outlen + 3]; byte[] sendBuf = new byte[outlen + 3];
Array.Copy(dataOut, 0, sendBuf, 3, outlen); Array.Copy(dataOut, 0, sendBuf, 3, outlen);
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
_local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); _local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint);
Receive(); Receive();
} }
@@ -118,6 +127,7 @@ namespace Shadowsocks.Controller
{ {
} }
} }
public void Close() public void Close()
{ {
try try
@@ -139,7 +149,6 @@ namespace Shadowsocks.Controller
} }
} }
// cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054 // cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054
class LRUCache<K, V> where V : UDPRelay.UDPHandler class LRUCache<K, V> where V : UDPRelay.UDPHandler
{ {


+ 56
- 25
shadowsocks-csharp/Controller/Service/UpdateChecker.cs View File

@@ -1,12 +1,9 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.IO;
using SimpleJson;
using Newtonsoft.Json.Linq;
using Shadowsocks.Model; using Shadowsocks.Model;
using Shadowsocks.Util; using Shadowsocks.Util;
@@ -26,7 +23,35 @@ namespace Shadowsocks.Controller
public string LatestVersionLocalName; public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted; public event EventHandler CheckUpdateCompleted;
public const string Version = "2.5.8";
public const string Version = "2.5.8.2";
private class CheckUpdateTimer : System.Timers.Timer
{
public Configuration config;
public CheckUpdateTimer(int p) : base(p)
{
}
}
public void CheckUpdate(Configuration config, int delay)
{
CheckUpdateTimer timer = new CheckUpdateTimer(delay);
timer.AutoReset = false;
timer.Elapsed += Timer_Elapsed;
timer.config = config;
timer.Enabled = true;
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
CheckUpdateTimer timer = (CheckUpdateTimer)sender;
Configuration config = timer.config;
timer.Elapsed -= Timer_Elapsed;
timer.Enabled = false;
timer.Dispose();
CheckUpdate(config);
}
public void CheckUpdate(Configuration config) public void CheckUpdate(Configuration config)
{ {
@@ -34,6 +59,7 @@ namespace Shadowsocks.Controller
try try
{ {
Logging.Debug("Checking updates...");
WebClient http = CreateWebClient(); WebClient http = CreateWebClient();
http.DownloadStringCompleted += http_DownloadStringCompleted; http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL)); http.DownloadStringAsync(new Uri(UpdateURL));
@@ -50,26 +76,28 @@ namespace Shadowsocks.Controller
{ {
string response = e.Result; string response = e.Result;
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
JArray result = JArray.Parse(response);
List<Asset> asserts = new List<Asset>(); List<Asset> asserts = new List<Asset>();
foreach (JsonObject release in result)
if (result != null)
{ {
if ((bool)release["prerelease"])
{
continue;
}
foreach (JsonObject asset in (JsonArray)release["assets"])
foreach (JObject release in result)
{ {
Asset ass = new Asset();
ass.Parse(asset);
if (ass.IsNewVersion(Version))
if ((bool)release["prerelease"])
{
continue;
}
foreach (JObject asset in (JArray)release["assets"])
{ {
asserts.Add(ass);
Asset ass = new Asset();
ass.Parse(asset);
if (ass.IsNewVersion(Version))
{
asserts.Add(ass);
}
} }
} }
} }
if (asserts.Count != 0) if (asserts.Count != 0)
{ {
SortByVersions(asserts); SortByVersions(asserts);
@@ -81,9 +109,13 @@ namespace Shadowsocks.Controller
startDownload(); startDownload();
} }
else if (CheckUpdateCompleted != null)
else
{ {
CheckUpdateCompleted(this, new EventArgs());
Logging.Debug("No update is available");
if (CheckUpdateCompleted != null)
{
CheckUpdateCompleted(this, new EventArgs());
}
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -96,8 +128,7 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
string temppath = Utils.GetTempPath();
LatestVersionLocalName = Path.Combine(temppath, LatestVersionName);
LatestVersionLocalName = Utils.GetTempPath(LatestVersionName);
WebClient http = CreateWebClient(); WebClient http = CreateWebClient();
http.DownloadFileCompleted += Http_DownloadFileCompleted; http.DownloadFileCompleted += Http_DownloadFileCompleted;
http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName); http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName);
@@ -112,11 +143,12 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
if(e.Error != null)
if (e.Error != null)
{ {
Logging.LogUsefulException(e.Error); Logging.LogUsefulException(e.Error);
return; return;
} }
Logging.Debug($"New version {LatestVersionNumber} found: {LatestVersionLocalName}");
if (CheckUpdateCompleted != null) if (CheckUpdateCompleted != null)
{ {
CheckUpdateCompleted(this, new EventArgs()); CheckUpdateCompleted(this, new EventArgs());
@@ -161,7 +193,7 @@ namespace Shadowsocks.Controller
return CompareVersion(version, currentVersion) > 0; return CompareVersion(version, currentVersion) > 0;
} }
public void Parse(JsonObject asset)
public void Parse(JObject asset)
{ {
name = (string)asset["name"]; name = (string)asset["name"];
browser_download_url = (string)asset["browser_download_url"]; browser_download_url = (string)asset["browser_download_url"];
@@ -207,6 +239,5 @@ namespace Shadowsocks.Controller
return Asset.CompareVersion(x.version, y.version); return Asset.CompareVersion(x.version, y.version);
} }
} }
} }
} }

+ 68
- 9
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -1,12 +1,17 @@
using System.IO;
using Shadowsocks.Model;
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Net.Sockets;
using Newtonsoft.Json;
using Shadowsocks.Controller.Strategy; using Shadowsocks.Controller.Strategy;
using System.Net;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -28,6 +33,9 @@ namespace Shadowsocks.Controller
public AvailabilityStatistics availabilityStatistics { get; private set; } public AvailabilityStatistics availabilityStatistics { get; private set; }
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }
public long inboundCounter = 0;
public long outboundCounter = 0;
private bool stopped = false; private bool stopped = false;
private bool _systemProxyIsDirty = false; private bool _systemProxyIsDirty = false;
@@ -60,7 +68,6 @@ namespace Shadowsocks.Controller
StartReleasingMemory(); StartReleasingMemory();
} }
public void Start() public void Start()
{ {
Reload(); Reload();
@@ -246,7 +253,7 @@ namespace Shadowsocks.Controller
public static string GetQRCode(Server server) public static string GetQRCode(Server server)
{ {
string parts = server.method + ":" + server.password + "@" + server.server + ":" + server.server_port; string parts = server.method + ":" + server.password + "@" + server.server + ":" + server.server_port;
string base64 = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
return "ss://" + base64; return "ss://" + base64;
} }
@@ -300,6 +307,16 @@ namespace Shadowsocks.Controller
Configuration.Save(_config); Configuration.Save(_config);
} }
public void UpdateInboundCounter(long n)
{
Interlocked.Add(ref inboundCounter, n);
}
public void UpdateOutboundCounter(long n)
{
Interlocked.Add(ref outboundCounter, n);
}
protected void Reload() protected void Reload()
{ {
// some logic in configuration updated the config when saving, we need to read it again // some logic in configuration updated the config when saving, we need to read it again
@@ -314,6 +331,7 @@ namespace Shadowsocks.Controller
{ {
_pacServer = new PACServer(); _pacServer = new PACServer();
_pacServer.PACFileChanged += pacServer_PACFileChanged; _pacServer.PACFileChanged += pacServer_PACFileChanged;
_pacServer.UserRuleFileChanged += pacServer_UserRuleFileChanged;
} }
_pacServer.UpdateConfiguration(_config); _pacServer.UpdateConfiguration(_config);
if (gfwListUpdater == null) if (gfwListUpdater == null)
@@ -380,7 +398,7 @@ namespace Shadowsocks.Controller
} }
UpdateSystemProxy(); UpdateSystemProxy();
Util.Utils.ReleaseMemory(true);
Utils.ReleaseMemory(true);
} }
protected void SaveConfig(Configuration newConfig) protected void SaveConfig(Configuration newConfig)
@@ -424,6 +442,47 @@ namespace Shadowsocks.Controller
UpdatePACFromGFWListError(this, e); UpdatePACFromGFWListError(this, e);
} }
private void pacServer_UserRuleFileChanged(object sender, EventArgs e)
{
// TODO: this is a dirty hack. (from code GListUpdater.http_DownloadStringCompleted())
if (!File.Exists(Utils.GetTempPath("gfwlist.txt")))
{
UpdatePACFromGFWList();
return;
}
List<string> lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath("gfwlist.txt")));
if (File.Exists(PACServer.USER_RULE_FILE))
{
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string rule in rules)
{
if (rule.StartsWith("!") || rule.StartsWith("["))
continue;
lines.Add(rule);
}
}
string abpContent;
if (File.Exists(PACServer.USER_ABP_FILE))
{
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
}
else
{
abpContent = Utils.UnGzip(Resources.abp_js);
}
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
if (File.Exists(PACServer.PAC_FILE))
{
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
if (original == abpContent)
{
return;
}
}
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8);
}
private void StartReleasingMemory() private void StartReleasingMemory()
{ {
_ramThread = new Thread(new ThreadStart(ReleaseMemory)); _ramThread = new Thread(new ThreadStart(ReleaseMemory));
@@ -435,7 +494,7 @@ namespace Shadowsocks.Controller
{ {
while (true) while (true)
{ {
Util.Utils.ReleaseMemory(false);
Utils.ReleaseMemory(false);
Thread.Sleep(30 * 1000); Thread.Sleep(30 * 1000);
} }
} }


+ 5
- 5
shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs View File

@@ -132,14 +132,14 @@ namespace Shadowsocks.Controller.Strategy
if (_currentServer == null || max.score - _currentServer.score > 200) if (_currentServer == null || max.score - _currentServer.score > 200)
{ {
_currentServer = max; _currentServer = max;
Console.WriteLine("HA switching to server: {0}", _currentServer.server.FriendlyName());
Logging.Info($"HA switching to server: {_currentServer.server.FriendlyName()}");
} }
} }
} }
public void UpdateLatency(Model.Server server, TimeSpan latency) public void UpdateLatency(Model.Server server, TimeSpan latency)
{ {
Logging.Debug(String.Format("latency: {0} {1}", server.FriendlyName(), latency));
Logging.Debug($"latency: {server.FriendlyName()} {latency}");
ServerStatus status; ServerStatus status;
if (_serverStatus.TryGetValue(server, out status)) if (_serverStatus.TryGetValue(server, out status))
@@ -151,7 +151,7 @@ namespace Shadowsocks.Controller.Strategy
public void UpdateLastRead(Model.Server server) public void UpdateLastRead(Model.Server server)
{ {
Logging.Debug(String.Format("last read: {0}", server.FriendlyName()));
Logging.Debug($"last read: {server.FriendlyName()}");
ServerStatus status; ServerStatus status;
if (_serverStatus.TryGetValue(server, out status)) if (_serverStatus.TryGetValue(server, out status))
@@ -162,7 +162,7 @@ namespace Shadowsocks.Controller.Strategy
public void UpdateLastWrite(Model.Server server) public void UpdateLastWrite(Model.Server server)
{ {
Logging.Debug(String.Format("last write: {0}", server.FriendlyName()));
Logging.Debug($"last write: {server.FriendlyName()}");
ServerStatus status; ServerStatus status;
if (_serverStatus.TryGetValue(server, out status)) if (_serverStatus.TryGetValue(server, out status))
@@ -173,7 +173,7 @@ namespace Shadowsocks.Controller.Strategy
public void SetFailure(Model.Server server) public void SetFailure(Model.Server server)
{ {
Logging.Debug(String.Format("failure: {0}", server.FriendlyName()));
Logging.Debug($"failure: {server.FriendlyName()}");
ServerStatus status; ServerStatus status;
if (_serverStatus.TryGetValue(server, out status)) if (_serverStatus.TryGetValue(server, out status))


+ 2
- 1
shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs View File

@@ -4,7 +4,9 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Threading; using System.Threading;

using Newtonsoft.Json; using Newtonsoft.Json;

using Shadowsocks.Model; using Shadowsocks.Model;


namespace Shadowsocks.Controller.Strategy namespace Shadowsocks.Controller.Strategy
@@ -147,6 +149,5 @@ namespace Shadowsocks.Controller.Strategy
{ {
//TODO: combine this part of data with ICMP statics //TODO: combine this part of data with ICMP statics
} }

} }
} }

+ 37
- 7
shadowsocks-csharp/Controller/System/AutoStartup.cs View File

@@ -6,21 +6,23 @@ namespace Shadowsocks.Controller
{ {
class AutoStartup class AutoStartup
{ {
static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode();
public static bool Set(bool enabled) public static bool Set(bool enabled)
{ {
RegistryKey runKey = null;
try try
{ {
string path = Application.ExecutablePath; string path = Application.ExecutablePath;
RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
if (enabled) if (enabled)
{ {
runKey.SetValue("Shadowsocks", path);
runKey.SetValue(Key, path);
} }
else else
{ {
runKey.DeleteValue("Shadowsocks");
runKey.DeleteValue(Key);
} }
runKey.Close();
return true; return true;
} }
catch (Exception e) catch (Exception e)
@@ -28,20 +30,39 @@ namespace Shadowsocks.Controller
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
return false; return false;
} }
finally
{
if (runKey != null)
{
try { runKey.Close(); }
catch (Exception e)
{ Logging.LogUsefulException(e); }
}
}
} }
public static bool Check() public static bool Check()
{ {
RegistryKey runKey = null;
try try
{ {
string path = Application.ExecutablePath; string path = Application.ExecutablePath;
RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run");
runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
string[] runList = runKey.GetValueNames(); string[] runList = runKey.GetValueNames();
runKey.Close();
foreach (string item in runList) foreach (string item in runList)
{ {
if (item.Equals("Shadowsocks"))
if (item.Equals(Key))
return true; return true;
else if (item.Equals("Shadowsocks")) // Compatibility with older versions
{
string value = Convert.ToString(runKey.GetValue(item));
if (path.Equals(value, StringComparison.InvariantCultureIgnoreCase))
{
runKey.DeleteValue(item);
runKey.SetValue(Key, path);
return true;
}
}
} }
return false; return false;
} }
@@ -50,6 +71,15 @@ namespace Shadowsocks.Controller
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
return false; return false;
} }
finally
{
if (runKey != null)
{
try { runKey.Close(); }
catch(Exception e)
{ Logging.LogUsefulException(e); }
}
}
} }
} }
} }

BIN
shadowsocks-csharp/Data/libsscrypto.dll.gz View File


+ 3
- 3
shadowsocks-csharp/Data/privoxy_conf.txt View File

@@ -1,5 +1,5 @@
listen-address __POLIPO_BIND_IP__:8123
listen-address __POLIPO_BIND_IP__:__POLIPO_BIND_PORT__
show-on-task-bar 0 show-on-task-bar 0
activity-animation 0 activity-animation 0
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
hide-console
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
hide-console

+ 1
- 1
shadowsocks-csharp/Encryption/EncryptorBase.cs View File

@@ -24,7 +24,7 @@ namespace Shadowsocks.Encryption
protected byte[] GetPasswordHash() protected byte[] GetPasswordHash()
{ {
byte[] inputBytes = Encoding.UTF8.GetBytes(Password); byte[] inputBytes = Encoding.UTF8.GetBytes(Password);
byte[] hash = MD5.Create().ComputeHash(inputBytes);
byte[] hash = MbedTLS.MD5(inputBytes);
return hash; return hash;
} }


+ 1
- 5
shadowsocks-csharp/Encryption/EncryptorFactory.cs View File

@@ -13,10 +13,6 @@ namespace Shadowsocks.Encryption
static EncryptorFactory() static EncryptorFactory()
{ {
_registeredEncryptors = new Dictionary<string, Type>(); _registeredEncryptors = new Dictionary<string, Type>();
foreach (string method in TableEncryptor.SupportedCiphers())
{
_registeredEncryptors.Add(method, typeof(TableEncryptor));
}
foreach (string method in PolarSSLEncryptor.SupportedCiphers()) foreach (string method in PolarSSLEncryptor.SupportedCiphers())
{ {
_registeredEncryptors.Add(method, typeof(PolarSSLEncryptor)); _registeredEncryptors.Add(method, typeof(PolarSSLEncryptor));
@@ -31,7 +27,7 @@ namespace Shadowsocks.Encryption
{ {
if (string.IsNullOrEmpty(method)) if (string.IsNullOrEmpty(method))
{ {
method = "table";
method = "aes-256-cfb";
} }
method = method.ToLowerInvariant(); method = method.ToLowerInvariant();
Type t = _registeredEncryptors[method]; Type t = _registeredEncryptors[method];


+ 16
- 13
shadowsocks-csharp/Encryption/IVEncryptor.cs View File

@@ -62,18 +62,22 @@ namespace Shadowsocks.Encryption
} }
keyLen = ciphers[_method][0]; keyLen = ciphers[_method][0];
ivLen = ciphers[_method][1]; ivLen = ciphers[_method][1];
if (CachedKeys.ContainsKey(k))
if (!CachedKeys.ContainsKey(k))
{ {
_key = CachedKeys[k];
}
else
{
byte[] passbuf = Encoding.UTF8.GetBytes(password);
_key = new byte[32];
byte[] iv = new byte[16];
bytesToKey(passbuf, _key);
CachedKeys[k] = _key;
lock (CachedKeys)
{
if (!CachedKeys.ContainsKey(k))
{
byte[] passbuf = Encoding.UTF8.GetBytes(password);
_key = new byte[32];
byte[] iv = new byte[16];
bytesToKey(passbuf, _key);
CachedKeys[k] = _key;
}
}
} }
if (_key == null)
_key = CachedKeys[k];
} }
protected void bytesToKey(byte[] password, byte[] key) protected void bytesToKey(byte[] password, byte[] key)
@@ -83,16 +87,15 @@ namespace Shadowsocks.Encryption
byte[] md5sum = null; byte[] md5sum = null;
while (i < key.Length) while (i < key.Length)
{ {
MD5 md5 = MD5.Create();
if (i == 0) if (i == 0)
{ {
md5sum = md5.ComputeHash(password);
md5sum = MbedTLS.MD5(password);
} }
else else
{ {
md5sum.CopyTo(result, 0); md5sum.CopyTo(result, 0);
password.CopyTo(result, md5sum.Length); password.CopyTo(result, md5sum.Length);
md5sum = md5.ComputeHash(result);
md5sum = MbedTLS.MD5(result);
} }
md5sum.CopyTo(key, i); md5sum.CopyTo(key, i);
i += md5sum.Length; i += md5sum.Length;


+ 65
- 0
shadowsocks-csharp/Encryption/MbedTLS.cs View File

@@ -0,0 +1,65 @@
using System;
using System.IO;
using System.Runtime.InteropServices;

using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;

namespace Shadowsocks.Encryption
{
public class MbedTLS
{
const string DLLNAME = "libsscrypto";

static MbedTLS()
{
string dllPath = Utils.GetTempPath("libsscrypto.dll");
try
{
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
}
catch (IOException)
{
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
LoadLibrary(dllPath);
}

[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string path);

public const int MD5_CTX_SIZE = 88;

public static byte[] MD5(byte[] input)
{
IntPtr ctx = Marshal.AllocHGlobal(MD5_CTX_SIZE);
byte[] output = new byte[16];
MbedTLS.md5_init(ctx);
MbedTLS.md5_starts(ctx);
MbedTLS.md5_update(ctx, input, (uint)input.Length);
MbedTLS.md5_finish(ctx, output);
MbedTLS.md5_free(ctx);
Marshal.FreeHGlobal(ctx);
return output;
}

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void md5_init(IntPtr ctx);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void md5_free(IntPtr ctx);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void md5_starts(IntPtr ctx);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void md5_update(IntPtr ctx, byte[] input, uint ilen);

[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void md5_finish(IntPtr ctx, byte[] output);
}
}

+ 7
- 10
shadowsocks-csharp/Encryption/PolarSSL.cs View File

@@ -1,11 +1,10 @@
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Encryption namespace Shadowsocks.Encryption
{ {
@@ -19,8 +18,7 @@ namespace Shadowsocks.Encryption
static PolarSSL() static PolarSSL()
{ {
string tempPath = Utils.GetTempPath();
string dllPath = tempPath + "/libsscrypto.dll";
string dllPath = Utils.GetTempPath("libsscrypto.dll");
try try
{ {
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
@@ -30,7 +28,7 @@ namespace Shadowsocks.Encryption
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e.ToString());
Logging.LogUsefulException(e);
} }
LoadLibrary(dllPath); LoadLibrary(dllPath);
} }
@@ -63,6 +61,5 @@ namespace Shadowsocks.Encryption
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static int arc4_crypt(IntPtr ctx, int length, byte[] input, byte[] output); public extern static int arc4_crypt(IntPtr ctx, int length, byte[] input, byte[] output);
} }
} }

+ 1
- 2
shadowsocks-csharp/Encryption/PolarSSLEncryptor.cs View File

@@ -26,7 +26,6 @@ namespace Shadowsocks.Encryption
{"aes-128-cfb", new int[]{16, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, {"aes-128-cfb", new int[]{16, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}},
{"aes-192-cfb", new int[]{24, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, {"aes-192-cfb", new int[]{24, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}},
{"aes-256-cfb", new int[]{32, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, {"aes-256-cfb", new int[]{32, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}},
{"rc4", new int[]{16, 0, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}},
{"rc4-md5", new int[]{16, 16, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, {"rc4-md5", new int[]{16, 16, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}},
}; };
@@ -61,7 +60,7 @@ namespace Shadowsocks.Encryption
realkey = new byte[keyLen]; realkey = new byte[keyLen];
Array.Copy(_key, 0, temp, 0, keyLen); Array.Copy(_key, 0, temp, 0, keyLen);
Array.Copy(iv, 0, temp, keyLen, ivLen); Array.Copy(iv, 0, temp, keyLen, ivLen);
realkey = MD5.Create().ComputeHash(temp);
realkey = MbedTLS.MD5(temp);
} }
else else
{ {


+ 10
- 10
shadowsocks-csharp/Encryption/Sodium.cs View File

@@ -1,11 +1,10 @@
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Encryption namespace Shadowsocks.Encryption
{ {
@@ -15,8 +14,7 @@ namespace Shadowsocks.Encryption
static Sodium() static Sodium()
{ {
string tempPath = Utils.GetTempPath();
string dllPath = tempPath + "/libsscrypto.dll";
string dllPath = Utils.GetTempPath("libsscrypto.dll");
try try
{ {
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
@@ -26,7 +24,7 @@ namespace Shadowsocks.Encryption
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e.ToString());
Logging.LogUsefulException(e);
} }
LoadLibrary(dllPath); LoadLibrary(dllPath);
} }
@@ -40,11 +38,13 @@ namespace Shadowsocks.Encryption
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] [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 extern static 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);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen, public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen,
byte[] input, int ioff, uint ilen, byte[] input, int ioff, uint ilen,
byte[] output); byte[] output);
} }
} }

+ 5
- 0
shadowsocks-csharp/Encryption/SodiumEncryptor.cs View File

@@ -10,6 +10,7 @@ namespace Shadowsocks.Encryption
{ {
const int CIPHER_SALSA20 = 1; const int CIPHER_SALSA20 = 1;
const int CIPHER_CHACHA20 = 2; const int CIPHER_CHACHA20 = 2;
const int CIPHER_CHACHA20_IETF = 3;
const int SODIUM_BLOCK_SIZE = 64; const int SODIUM_BLOCK_SIZE = 64;
@@ -29,6 +30,7 @@ namespace Shadowsocks.Encryption
private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> { private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> {
{"salsa20", new int[]{32, 8, CIPHER_SALSA20, PolarSSL.AES_CTX_SIZE}}, {"salsa20", new int[]{32, 8, CIPHER_SALSA20, PolarSSL.AES_CTX_SIZE}},
{"chacha20", new int[]{32, 8, CIPHER_CHACHA20, PolarSSL.AES_CTX_SIZE}}, {"chacha20", new int[]{32, 8, CIPHER_CHACHA20, PolarSSL.AES_CTX_SIZE}},
{"chacha20-ietf", new int[]{32, 12, CIPHER_CHACHA20_IETF, PolarSSL.AES_CTX_SIZE}},
}; };
protected override Dictionary<string, int[]> getCiphers() protected override Dictionary<string, int[]> getCiphers()
@@ -75,6 +77,9 @@ namespace Shadowsocks.Encryption
case CIPHER_CHACHA20: case CIPHER_CHACHA20:
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break; break;
case CIPHER_CHACHA20_IETF:
Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key);
break;
} }
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
padding += length; padding += length;


+ 0
- 106
shadowsocks-csharp/Encryption/TableEncryptor.cs View File

@@ -1,106 +0,0 @@
using System;
using System.Collections.Generic;
namespace Shadowsocks.Encryption
{
public class TableEncryptor
: EncryptorBase
{
public TableEncryptor(string method, string password, bool onetimeauth, bool isudp)
: base(method, password, onetimeauth, isudp)
{
byte[] hash = GetPasswordHash();
// TODO endian
ulong a = BitConverter.ToUInt64(hash, 0);
for (int i = 0; i < 256; i++)
{
_encryptTable[i] = (byte)i;
}
for (int i = 1; i < 1024; i++)
{
_encryptTable = MergeSort(_encryptTable, a, i);
}
for (int i = 0; i < 256; i++)
{
_decryptTable[_encryptTable[i]] = (byte)i;
}
}
public static List<string> SupportedCiphers()
{
return new List<string>(new string[]{"table"});
}
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
byte[] result = new byte[length];
for (int i = 0; i < length; i++)
{
outbuf[i] = _encryptTable[buf[i]];
}
outlength = length;
}
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
byte[] result = new byte[length];
for (int i = 0; i < length; i++)
{
outbuf[i] = _decryptTable[buf[i]];
}
outlength = length;
}
private readonly byte[] _encryptTable = new byte[256];
private readonly byte[] _decryptTable = new byte[256];
private static long Compare(byte x, byte y, ulong a, int i)
{
return (long)(a % (ulong)(x + i)) - (long)(a % (ulong)(y + i));
}
private byte[] MergeSort(byte[] array, ulong a, int j)
{
if (array.Length == 1)
{
return array;
}
int middle = array.Length / 2;
byte[] left = new byte[middle];
for (int i = 0; i < middle; i++)
{
left[i] = array[i];
}
byte[] right = new byte[array.Length - middle];
for (int i = 0; i < array.Length - middle; i++)
{
right[i] = array[i + middle];
}
left = MergeSort(left, a, j);
right = MergeSort(right, a, j);
int leftptr = 0;
int rightptr = 0;
byte[] sorted = new byte[array.Length];
for (int k = 0; k < array.Length; k++)
{
if (rightptr == right.Length || ((leftptr < left.Length) && (Compare(left[leftptr], right[rightptr], a, j) <= 0)))
{
sorted[k] = left[leftptr];
leftptr++;
}
else if (leftptr == left.Length || ((rightptr < right.Length) && (Compare(right[rightptr], left[leftptr], a, j)) <= 0))
{
sorted[k] = right[rightptr];
rightptr++;
}
}
return sorted;
}
public override void Dispose()
{
}
}
}

+ 11
- 57
shadowsocks-csharp/Model/Configuration.cs View File

@@ -1,10 +1,9 @@
using Shadowsocks.Controller;
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using System.Windows.Forms;
using SimpleJson;
using Shadowsocks.Controller;
using Newtonsoft.Json;
namespace Shadowsocks.Model namespace Shadowsocks.Model
{ {
@@ -32,13 +31,9 @@ namespace Shadowsocks.Model
public Server GetCurrentServer() public Server GetCurrentServer()
{ {
if (index >= 0 && index < configs.Count) if (index >= 0 && index < configs.Count)
{
return configs[index]; return configs[index];
}
else else
{
return GetDefaultServer(); return GetDefaultServer();
}
} }
public static void CheckServer(Server server) public static void CheckServer(Server server)
@@ -53,27 +48,18 @@ namespace Shadowsocks.Model
try try
{ {
string configContent = File.ReadAllText(CONFIG_FILE); string configContent = File.ReadAllText(CONFIG_FILE);
Configuration config = SimpleJson.SimpleJson.DeserializeObject<Configuration>(configContent, new JsonSerializerStrategy());
Configuration config = JsonConvert.DeserializeObject<Configuration>(configContent);
config.isDefault = false; config.isDefault = false;
if (config.localPort == 0) if (config.localPort == 0)
{
config.localPort = 1080; config.localPort = 1080;
}
if (config.index == -1)
{
if (config.strategy == null)
{
config.index = 0;
}
}
if (config.index == -1 && config.strategy == null)
config.index = 0;
return config; return config;
} }
catch (Exception e) catch (Exception e)
{ {
if (!(e is FileNotFoundException)) if (!(e is FileNotFoundException))
{
Console.WriteLine(e);
}
Logging.LogUsefulException(e);
return new Configuration return new Configuration
{ {
index = 0, index = 0,
@@ -91,26 +77,17 @@ namespace Shadowsocks.Model
public static void Save(Configuration config) public static void Save(Configuration config)
{ {
if (config.index >= config.configs.Count) if (config.index >= config.configs.Count)
{
config.index = config.configs.Count - 1; config.index = config.configs.Count - 1;
}
if (config.index < -1) if (config.index < -1)
{
config.index = -1; config.index = -1;
}
if (config.index == -1)
{
if (config.strategy == null)
{
config.index = 0;
}
}
if (config.index == -1 && config.strategy == null)
config.index = 0;
config.isDefault = false; config.isDefault = false;
try try
{ {
using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create))) using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create)))
{ {
string jsonString = SimpleJson.SimpleJson.SerializeObject(config);
string jsonString = JsonConvert.SerializeObject(config, Formatting.Indented);
sw.Write(jsonString); sw.Write(jsonString);
sw.Flush(); sw.Flush();
} }
@@ -129,55 +106,32 @@ namespace Shadowsocks.Model
private static void Assert(bool condition) private static void Assert(bool condition)
{ {
if (!condition) if (!condition)
{
throw new Exception(I18N.GetString("assertion failure")); throw new Exception(I18N.GetString("assertion failure"));
}
} }
public static void CheckPort(int port) public static void CheckPort(int port)
{ {
if (port <= 0 || port > 65535) if (port <= 0 || port > 65535)
{
throw new ArgumentException(I18N.GetString("Port out of range")); throw new ArgumentException(I18N.GetString("Port out of range"));
}
} }
public static void CheckLocalPort(int port) public static void CheckLocalPort(int port)
{ {
CheckPort(port); CheckPort(port);
if (port == 8123) if (port == 8123)
{
throw new ArgumentException(I18N.GetString("Port can't be 8123")); throw new ArgumentException(I18N.GetString("Port can't be 8123"));
}
} }
private static void CheckPassword(string password) private static void CheckPassword(string password)
{ {
if (string.IsNullOrEmpty(password)) if (string.IsNullOrEmpty(password))
{
throw new ArgumentException(I18N.GetString("Password can not be blank")); throw new ArgumentException(I18N.GetString("Password can not be blank"));
}
} }
private static void CheckServer(string server) private static void CheckServer(string server)
{ {
if (string.IsNullOrEmpty(server)) if (string.IsNullOrEmpty(server))
{
throw new ArgumentException(I18N.GetString("Server IP can not be blank")); throw new ArgumentException(I18N.GetString("Server IP can not be blank"));
}
}
private class JsonSerializerStrategy : SimpleJson.PocoJsonSerializerStrategy
{
// convert string to int
public override object DeserializeObject(object value, Type type)
{
if (type == typeof(Int32) && value.GetType() == typeof(string))
{
return Int32.Parse(value.ToString());
}
return base.DeserializeObject(value, type);
}
} }
} }
} }

+ 31
- 8
shadowsocks-csharp/Model/LogViewerConfig.cs View File

@@ -1,5 +1,7 @@
using System;
using Shadowsocks.View;
using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms;
namespace Shadowsocks.Model namespace Shadowsocks.Model
{ {
@@ -13,16 +15,37 @@ namespace Shadowsocks.Model
public bool topMost; public bool topMost;
public bool wrapText; public bool wrapText;
public bool toolbarShown; public bool toolbarShown;
public int width;
public int height;
public int top;
public int left;
public LogViewerConfig() public LogViewerConfig()
{ {
this.fontName = "Consolas";
this.fontSize = 8;
this.bgColor = "black";
this.textColor = "white";
this.topMost = false;
this.wrapText = false;
this.toolbarShown = false;
fontName = "Consolas";
fontSize = 8;
bgColor = "black";
textColor = "white";
topMost = false;
wrapText = false;
toolbarShown = false;
width = 600;
height = 400;
left = GetBestLeft();
top = GetBestTop();
}
// Use GetBestTop() and GetBestLeft() to ensure the log viwer form can be always display IN screen.
public int GetBestLeft()
{
width = (width >= 400) ? width : 400; // set up the minimum size
return Screen.PrimaryScreen.WorkingArea.Width - width;
}
public int GetBestTop()
{
height = (height >= 200) ? height : 200; // set up the minimum size
return Screen.PrimaryScreen.WorkingArea.Height - height;
} }
public Font GetFont() public Font GetFont()


+ 15
- 19
shadowsocks-csharp/Model/Server.cs View File

@@ -1,12 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Text; using System.Text;
using System.IO;
using System.Diagnostics;
using SimpleJson;
using Shadowsocks.Controller;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Shadowsocks.Controller;
namespace Shadowsocks.Model namespace Shadowsocks.Model
{ {
[Serializable] [Serializable]
@@ -17,7 +14,7 @@ namespace Shadowsocks.Model
public string password; public string password;
public string method; public string method;
public string remarks; public string remarks;
public bool one_time_auth;
public bool auth;
public override int GetHashCode() public override int GetHashCode()
{ {
@@ -27,7 +24,7 @@ namespace Shadowsocks.Model
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
Server o2 = (Server)obj; Server o2 = (Server)obj;
return this.server == o2.server && this.server_port == o2.server_port;
return server == o2.server && server_port == o2.server_port;
} }
public string FriendlyName() public string FriendlyName()
@@ -48,12 +45,12 @@ namespace Shadowsocks.Model
public Server() public Server()
{ {
this.server = "";
this.server_port = 8388;
this.method = "aes-256-cfb";
this.password = "";
this.remarks = "";
this.one_time_auth = false;
server = "";
server_port = 8388;
method = "aes-256-cfb";
password = "";
remarks = "";
auth = false;
} }
public Server(string ssURL) : this() public Server(string ssURL) : this()
@@ -65,7 +62,7 @@ namespace Shadowsocks.Model
{ {
try try
{ {
bytes = System.Convert.FromBase64String(base64);
bytes = Convert.FromBase64String(base64);
} }
catch (FormatException) catch (FormatException)
{ {
@@ -83,16 +80,15 @@ namespace Shadowsocks.Model
string afterAt = data.Substring(indexLastAt + 1); string afterAt = data.Substring(indexLastAt + 1);
int indexLastColon = afterAt.LastIndexOf(':'); int indexLastColon = afterAt.LastIndexOf(':');
this.server_port = int.Parse(afterAt.Substring(indexLastColon + 1));
this.server = afterAt.Substring(0, indexLastColon);
server_port = int.Parse(afterAt.Substring(indexLastColon + 1));
server = afterAt.Substring(0, indexLastColon);
string beforeAt = data.Substring(0, indexLastAt); string beforeAt = data.Substring(0, indexLastAt);
string[] parts = beforeAt.Split(new[] { ':' }); string[] parts = beforeAt.Split(new[] { ':' });
this.method = parts[0];
this.password = parts[1];
method = parts[0];
password = parts[1];
//TODO: read one_time_auth //TODO: read one_time_auth
} }
catch (IndexOutOfRangeException) catch (IndexOutOfRangeException)
{ {


+ 7
- 8
shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs View File

@@ -3,17 +3,17 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Shadowsocks.Controller;
using Shadowsocks.Controller.Strategy;
using SimpleJson;

using Newtonsoft.Json; using Newtonsoft.Json;


using Shadowsocks.Controller;

namespace Shadowsocks.Model namespace Shadowsocks.Model
{ {
[Serializable] [Serializable]
public class StatisticsStrategyConfiguration public class StatisticsStrategyConfiguration
{ {
public static readonly string ID = "com.shadowsocks.strategy.statistics";
public static readonly string ID = "com.shadowsocks.strategy.statistics";
private bool _statisticsEnabled = true; private bool _statisticsEnabled = true;
private bool _byIsp = false; private bool _byIsp = false;
private bool _byHourOfDay = false; private bool _byHourOfDay = false;
@@ -21,7 +21,6 @@ namespace Shadowsocks.Model
private int _dataCollectionMinutes = 10; private int _dataCollectionMinutes = 10;
private int _repeatTimesNum = 4; private int _repeatTimesNum = 4;



private const string ConfigFile = "statistics-config.json"; private const string ConfigFile = "statistics-config.json";


public static StatisticsStrategyConfiguration Load() public static StatisticsStrategyConfiguration Load()
@@ -32,7 +31,7 @@ namespace Shadowsocks.Model
var configuration = JsonConvert.DeserializeObject<StatisticsStrategyConfiguration>(content); var configuration = JsonConvert.DeserializeObject<StatisticsStrategyConfiguration>(content);
return configuration; return configuration;
} }
catch (FileNotFoundException e)
catch (FileNotFoundException)
{ {
var configuration = new StatisticsStrategyConfiguration(); var configuration = new StatisticsStrategyConfiguration();
Save(configuration); Save(configuration);
@@ -62,10 +61,10 @@ namespace Shadowsocks.Model


public StatisticsStrategyConfiguration() public StatisticsStrategyConfiguration()
{ {
var availabilityStatisticsType = typeof (AvailabilityStatistics);
var availabilityStatisticsType = typeof(AvailabilityStatistics);
var statisticsData = availabilityStatisticsType.GetNestedType("StatisticsData"); var statisticsData = availabilityStatisticsType.GetNestedType("StatisticsData");
var properties = statisticsData.GetFields(BindingFlags.Instance | BindingFlags.Public); var properties = statisticsData.GetFields(BindingFlags.Instance | BindingFlags.Public);
Calculations = properties.ToDictionary(p => p.Name, _ => (float) 0);
Calculations = properties.ToDictionary(p => p.Name, _ => (float)0);
} }


public bool StatisticsEnabled public bool StatisticsEnabled


+ 14
- 10
shadowsocks-csharp/Program.cs View File

@@ -1,13 +1,13 @@
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.View;
using System;
using System.Collections.Generic;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using Shadowsocks.Controller;
using Shadowsocks.Util;
using Shadowsocks.View;
namespace Shadowsocks namespace Shadowsocks
{ {
static class Program static class Program
@@ -18,7 +18,7 @@ namespace Shadowsocks
[STAThread] [STAThread]
static void Main() static void Main()
{ {
Util.Utils.ReleaseMemory(true);
Utils.ReleaseMemory(true);
using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode()))
{ {
Application.EnableVisualStyles(); Application.EnableVisualStyles();
@@ -37,15 +37,19 @@ namespace Shadowsocks
return; return;
} }
Directory.SetCurrentDirectory(Application.StartupPath); Directory.SetCurrentDirectory(Application.StartupPath);
#if !DEBUG
#if DEBUG
Logging.OpenLogFile();
// truncate privoxy log file while debugging
string privoxyLogFilename = Utils.GetTempPath("privoxy.log");
if (File.Exists(privoxyLogFilename))
using (new FileStream(privoxyLogFilename, FileMode.Truncate)) { }
#else
Logging.OpenLogFile(); Logging.OpenLogFile();
#endif #endif
ShadowsocksController controller = new ShadowsocksController(); ShadowsocksController controller = new ShadowsocksController();
MenuViewController viewController = new MenuViewController(controller); MenuViewController viewController = new MenuViewController(controller);
controller.Start(); controller.Start();
Application.Run(); Application.Run();
} }
} }


+ 56
- 13
shadowsocks-csharp/Util/Util.cs View File

@@ -1,32 +1,48 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using Shadowsocks.Controller;
namespace Shadowsocks.Util namespace Shadowsocks.Util
{ {
public class Utils public class Utils
{ {
private static string TempPath = null;
// return path to store temporary files // return path to store temporary files
public static string GetTempPath() public static string GetTempPath()
{ {
if (File.Exists(Application.StartupPath + "\\shadowsocks_portable_mode.txt"))
if (TempPath == null)
{ {
try
{
Directory.CreateDirectory(Application.StartupPath + "\\temp");
} catch (Exception e)
{
Console.WriteLine(e);
}
// don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log
return Application.StartupPath + "\\temp";
if (File.Exists(Path.Combine(Application.StartupPath, "shadowsocks_portable_mode.txt")))
try
{
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "temp"));
}
catch (Exception e)
{
TempPath = Path.GetTempPath();
Logging.LogUsefulException(e);
}
finally
{
// don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log
TempPath = Path.Combine(Application.StartupPath, "temp");
}
else
TempPath = Path.GetTempPath();
} }
return Path.GetTempPath();
return TempPath;
}
// return a full path with filename combined which pointed to the temporary directory
public static string GetTempPath(string filename)
{
return Path.Combine(GetTempPath(), filename);
} }
public static void ReleaseMemory(bool removePages) public static void ReleaseMemory(bool removePages)
@@ -82,6 +98,33 @@ namespace Shadowsocks.Util
} }
} }
public static string FormatBandwidth(long n)
{
float f = n;
string unit = "B";
if (f > 1024)
{
f = f / 1024;
unit = "KiB";
}
if (f > 1024)
{
f = f / 1024;
unit = "MiB";
}
if (f > 1024)
{
f = f / 1024;
unit = "GiB";
}
if (f > 1024)
{
f = f / 1024;
unit = "TiB";
}
return $"{f:0.##}{unit}";
}
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetProcessWorkingSetSize(IntPtr process, private static extern bool SetProcessWorkingSetSize(IntPtr process,


+ 2
- 3
shadowsocks-csharp/View/ConfigForm.Designer.cs View File

@@ -198,14 +198,13 @@
this.EncryptionSelect.ImeMode = System.Windows.Forms.ImeMode.NoControl; this.EncryptionSelect.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.EncryptionSelect.ItemHeight = 12; this.EncryptionSelect.ItemHeight = 12;
this.EncryptionSelect.Items.AddRange(new object[] { this.EncryptionSelect.Items.AddRange(new object[] {
"table",
"rc4-md5", "rc4-md5",
"salsa20", "salsa20",
"chacha20", "chacha20",
"chacha20-ietf",
"aes-256-cfb", "aes-256-cfb",
"aes-192-cfb", "aes-192-cfb",
"aes-128-cfb",
"rc4"});
"aes-128-cfb"});
this.EncryptionSelect.Location = new System.Drawing.Point(83, 87); this.EncryptionSelect.Location = new System.Drawing.Point(83, 87);
this.EncryptionSelect.Name = "EncryptionSelect"; this.EncryptionSelect.Name = "EncryptionSelect";
this.EncryptionSelect.Size = new System.Drawing.Size(160, 20); this.EncryptionSelect.Size = new System.Drawing.Size(160, 20);


+ 7
- 4
shadowsocks-csharp/View/ConfigForm.cs View File

@@ -79,12 +79,12 @@ namespace Shadowsocks.View
} }
Server server = new Server Server server = new Server
{ {
server = IPTextBox.Text,
server = IPTextBox.Text.Trim(),
server_port = int.Parse(ServerPortTextBox.Text), server_port = int.Parse(ServerPortTextBox.Text),
password = PasswordTextBox.Text, password = PasswordTextBox.Text,
method = EncryptionSelect.Text, method = EncryptionSelect.Text,
remarks = RemarksTextBox.Text, remarks = RemarksTextBox.Text,
one_time_auth = OneTimeAuth.Checked
auth = OneTimeAuth.Checked
}; };
int localPort = int.Parse(ProxyPortTextBox.Text); int localPort = int.Parse(ProxyPortTextBox.Text);
Configuration.CheckServer(server); Configuration.CheckServer(server);
@@ -117,7 +117,7 @@ namespace Shadowsocks.View
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString();
EncryptionSelect.Text = server.method ?? "aes-256-cfb"; EncryptionSelect.Text = server.method ?? "aes-256-cfb";
RemarksTextBox.Text = server.remarks; RemarksTextBox.Text = server.remarks;
OneTimeAuth.Checked = server.one_time_auth;
OneTimeAuth.Checked = server.auth;
} }
} }
@@ -188,7 +188,10 @@ namespace Shadowsocks.View
ServersListBox.SelectedIndex = _lastSelectedIndex; ServersListBox.SelectedIndex = _lastSelectedIndex;
return; return;
} }
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName();
if (_lastSelectedIndex >= 0)
{
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName();
}
UpdateMoveUpAndDownButton(); UpdateMoveUpAndDownButton();
LoadSelectedServer(); LoadSelectedServer();
_lastSelectedIndex = ServersListBox.SelectedIndex; _lastSelectedIndex = ServersListBox.SelectedIndex;


+ 12
- 11
shadowsocks-csharp/View/LogForm.Designer.cs View File

@@ -57,13 +57,13 @@
this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; this.LogMessageTextBox.ForeColor = System.Drawing.Color.White;
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 38);
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 40);
this.LogMessageTextBox.MaxLength = 2147483647; this.LogMessageTextBox.MaxLength = 2147483647;
this.LogMessageTextBox.Multiline = true; this.LogMessageTextBox.Multiline = true;
this.LogMessageTextBox.Name = "LogMessageTextBox"; this.LogMessageTextBox.Name = "LogMessageTextBox";
this.LogMessageTextBox.ReadOnly = true; this.LogMessageTextBox.ReadOnly = true;
this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.LogMessageTextBox.Size = new System.Drawing.Size(584, 377);
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 131);
this.LogMessageTextBox.TabIndex = 0; this.LogMessageTextBox.TabIndex = 0;
// //
// MainMenu // MainMenu
@@ -144,9 +144,9 @@
this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left))); | System.Windows.Forms.AnchorStyles.Left)));
this.TopMostCheckBox.AutoSize = true; this.TopMostCheckBox.AutoSize = true;
this.TopMostCheckBox.Location = new System.Drawing.Point(249, 3);
this.TopMostCheckBox.Location = new System.Drawing.Point(247, 3);
this.TopMostCheckBox.Name = "TopMostCheckBox"; this.TopMostCheckBox.Name = "TopMostCheckBox";
this.TopMostCheckBox.Size = new System.Drawing.Size(72, 23);
this.TopMostCheckBox.Size = new System.Drawing.Size(71, 25);
this.TopMostCheckBox.TabIndex = 3; this.TopMostCheckBox.TabIndex = 3;
this.TopMostCheckBox.Text = "&Top Most"; this.TopMostCheckBox.Text = "&Top Most";
this.TopMostCheckBox.UseVisualStyleBackColor = true; this.TopMostCheckBox.UseVisualStyleBackColor = true;
@@ -157,7 +157,7 @@
this.ChangeFontButton.AutoSize = true; this.ChangeFontButton.AutoSize = true;
this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); this.ChangeFontButton.Location = new System.Drawing.Point(84, 3);
this.ChangeFontButton.Name = "ChangeFontButton"; this.ChangeFontButton.Name = "ChangeFontButton";
this.ChangeFontButton.Size = new System.Drawing.Size(75, 23);
this.ChangeFontButton.Size = new System.Drawing.Size(75, 25);
this.ChangeFontButton.TabIndex = 2; this.ChangeFontButton.TabIndex = 2;
this.ChangeFontButton.Text = "&Font"; this.ChangeFontButton.Text = "&Font";
this.ChangeFontButton.UseVisualStyleBackColor = true; this.ChangeFontButton.UseVisualStyleBackColor = true;
@@ -168,7 +168,7 @@
this.CleanLogsButton.AutoSize = true; this.CleanLogsButton.AutoSize = true;
this.CleanLogsButton.Location = new System.Drawing.Point(3, 3); this.CleanLogsButton.Location = new System.Drawing.Point(3, 3);
this.CleanLogsButton.Name = "CleanLogsButton"; this.CleanLogsButton.Name = "CleanLogsButton";
this.CleanLogsButton.Size = new System.Drawing.Size(75, 23);
this.CleanLogsButton.Size = new System.Drawing.Size(75, 25);
this.CleanLogsButton.TabIndex = 1; this.CleanLogsButton.TabIndex = 1;
this.CleanLogsButton.Text = "&Clean Logs"; this.CleanLogsButton.Text = "&Clean Logs";
this.CleanLogsButton.UseVisualStyleBackColor = true; this.CleanLogsButton.UseVisualStyleBackColor = true;
@@ -181,7 +181,7 @@
this.WrapTextCheckBox.AutoSize = true; this.WrapTextCheckBox.AutoSize = true;
this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3); this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3);
this.WrapTextCheckBox.Name = "WrapTextCheckBox"; this.WrapTextCheckBox.Name = "WrapTextCheckBox";
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 23);
this.WrapTextCheckBox.Size = new System.Drawing.Size(76, 25);
this.WrapTextCheckBox.TabIndex = 0; this.WrapTextCheckBox.TabIndex = 0;
this.WrapTextCheckBox.Text = "&Wrap Text"; this.WrapTextCheckBox.Text = "&Wrap Text";
this.WrapTextCheckBox.UseVisualStyleBackColor = true; this.WrapTextCheckBox.UseVisualStyleBackColor = true;
@@ -199,7 +199,7 @@
this.tableLayoutPanel1.RowCount = 2; this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(590, 418);
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 174);
this.tableLayoutPanel1.TabIndex = 2; this.tableLayoutPanel1.TabIndex = 2;
// //
// ToolbarFlowLayoutPanel // ToolbarFlowLayoutPanel
@@ -212,16 +212,17 @@
this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3);
this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel"; this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel";
this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(584, 29);
this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 31);
this.ToolbarFlowLayoutPanel.TabIndex = 2; this.ToolbarFlowLayoutPanel.TabIndex = 2;
// //
// LogForm // LogForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(590, 418);
this.ClientSize = new System.Drawing.Size(384, 174);
this.Controls.Add(this.tableLayoutPanel1); this.Controls.Add(this.tableLayoutPanel1);
this.Menu = this.MainMenu; this.Menu = this.MainMenu;
this.MinimumSize = new System.Drawing.Size(400, 213);
this.Name = "LogForm"; this.Name = "LogForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Log Viewer"; this.Text = "Log Viewer";


+ 35
- 19
shadowsocks-csharp/View/LogForm.cs View File

@@ -1,16 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using Shadowsocks.Controller; using Shadowsocks.Controller;
using Shadowsocks.Properties; using Shadowsocks.Properties;
using Shadowsocks.Model; using Shadowsocks.Model;
using Shadowsocks.Util;
namespace Shadowsocks.View namespace Shadowsocks.View
{ {
@@ -27,17 +23,21 @@ namespace Shadowsocks.View
this.controller = controller; this.controller = controller;
this.filename = filename; this.filename = filename;
InitializeComponent(); InitializeComponent();
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
if (config == null) if (config == null)
{
config = new LogViewerConfig(); config = new LogViewerConfig();
topMostTrigger = config.topMost;
wrapTextTrigger = config.wrapText;
toolbarTrigger = config.toolbarShown;
LogMessageTextBox.BackColor = config.GetBackgroundColor();
LogMessageTextBox.ForeColor = config.GetTextColor();
LogMessageTextBox.Font = config.GetFont();
}
else {
topMostTrigger = config.topMost;
wrapTextTrigger = config.wrapText;
toolbarTrigger = config.toolbarShown;
LogMessageTextBox.BackColor = config.GetBackgroundColor();
LogMessageTextBox.ForeColor = config.GetTextColor();
LogMessageTextBox.Font = config.GetFont();
}
UpdateTexts(); UpdateTexts();
} }
@@ -57,7 +57,7 @@ namespace Shadowsocks.View
WrapTextMenuItem.Text = I18N.GetString("&Wrap Text"); WrapTextMenuItem.Text = I18N.GetString("&Wrap Text");
TopMostMenuItem.Text = I18N.GetString("&Top Most"); TopMostMenuItem.Text = I18N.GetString("&Top Most");
ShowToolbarMenuItem.Text = I18N.GetString("&Show Toolbar"); ShowToolbarMenuItem.Text = I18N.GetString("&Show Toolbar");
this.Text = I18N.GetString("Log Viewer");
Text = I18N.GetString("Log Viewer");
} }
private void Timer_Tick(object sender, EventArgs e) private void Timer_Tick(object sender, EventArgs e)
@@ -108,18 +108,30 @@ namespace Shadowsocks.View
lastOffset = reader.BaseStream.Position; lastOffset = reader.BaseStream.Position;
} }
this.Text = I18N.GetString("Log Viewer") +
$" [in: {Utils.FormatBandwidth(controller.inboundCounter)}, out: {Utils.FormatBandwidth(controller.outboundCounter)}]";
} }
private void LogForm_Load(object sender, EventArgs e) private void LogForm_Load(object sender, EventArgs e)
{ {
InitContent(); InitContent();
timer = new Timer(); timer = new Timer();
timer.Interval = 300; timer.Interval = 300;
timer.Tick += Timer_Tick; timer.Tick += Timer_Tick;
timer.Start(); timer.Start();
LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
if (config == null)
config = new LogViewerConfig();
Height = config.height;
Width = config.width;
Top = config.GetBestTop();
Left = config.GetBestLeft();
topMostTriggerLock = true; topMostTriggerLock = true;
this.TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
topMostTriggerLock = false; topMostTriggerLock = false;
wrapTextTriggerLock = true; wrapTextTriggerLock = true;
@@ -141,19 +153,23 @@ namespace Shadowsocks.View
config.SetFont(LogMessageTextBox.Font); config.SetFont(LogMessageTextBox.Font);
config.SetBackgroundColor(LogMessageTextBox.BackColor); config.SetBackgroundColor(LogMessageTextBox.BackColor);
config.SetTextColor(LogMessageTextBox.ForeColor); config.SetTextColor(LogMessageTextBox.ForeColor);
config.top = Top;
config.left = Left;
config.height = Height;
config.width = Width;
controller.SaveLogViewerConfig(config); controller.SaveLogViewerConfig(config);
} }
private void OpenLocationMenuItem_Click(object sender, EventArgs e) private void OpenLocationMenuItem_Click(object sender, EventArgs e)
{ {
string argument = "/select, \"" + filename + "\""; string argument = "/select, \"" + filename + "\"";
Console.WriteLine(argument);
Logging.Debug(argument);
System.Diagnostics.Process.Start("explorer.exe", argument); System.Diagnostics.Process.Start("explorer.exe", argument);
} }
private void ExitMenuItem_Click(object sender, EventArgs e) private void ExitMenuItem_Click(object sender, EventArgs e)
{ {
this.Close();
Close();
} }
private void LogForm_Shown(object sender, EventArgs e) private void LogForm_Shown(object sender, EventArgs e)
@@ -208,7 +224,7 @@ namespace Shadowsocks.View
} }
#endregion #endregion
#region Trigger the log messages wrapable, or not.
#region Trigger the log messages to wrapable, or not.
bool wrapTextTrigger = false; bool wrapTextTrigger = false;
bool wrapTextTriggerLock = false; bool wrapTextTriggerLock = false;
@@ -241,7 +257,7 @@ namespace Shadowsocks.View
} }
#endregion #endregion
#region Trigger this window top most, or not.
#region Trigger the window to top most, or not.
bool topMostTrigger = false; bool topMostTrigger = false;
bool topMostTriggerLock = false; bool topMostTriggerLock = false;
@@ -250,7 +266,7 @@ namespace Shadowsocks.View
topMostTriggerLock = true; topMostTriggerLock = true;
topMostTrigger = !topMostTrigger; topMostTrigger = !topMostTrigger;
this.TopMost = topMostTrigger;
TopMost = topMostTrigger;
TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
topMostTriggerLock = false; topMostTriggerLock = false;


+ 24
- 0
shadowsocks-csharp/View/LogForm.resx View File

@@ -117,7 +117,31 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<metadata name="LogMessageTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="MainMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="MainMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>
<metadata name="TopMostCheckBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ChangeFontButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="CleanLogsButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="WrapTextCheckBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="tableLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ToolbarFlowLayoutPanel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root> </root>

+ 77
- 18
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -1,16 +1,18 @@
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using ZXing; using ZXing;
using ZXing.Common; using ZXing.Common;
using ZXing.QrCode; using ZXing.QrCode;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.View namespace Shadowsocks.View
{ {
public class MenuViewController public class MenuViewController
@@ -44,6 +46,8 @@ namespace Shadowsocks.View
private MenuItem editOnlinePACItem; private MenuItem editOnlinePACItem;
private MenuItem autoCheckUpdatesToggleItem; private MenuItem autoCheckUpdatesToggleItem;
private ConfigForm configForm; private ConfigForm configForm;
private List<LogForm> logForms = new List<LogForm>();
private bool logFormsVisible = false;
private string _urlToOpen; private string _urlToOpen;
public MenuViewController(ShadowsocksController controller) public MenuViewController(ShadowsocksController controller)
@@ -66,7 +70,10 @@ namespace Shadowsocks.View
UpdateTrayIcon(); UpdateTrayIcon();
_notifyIcon.Visible = true; _notifyIcon.Visible = true;
_notifyIcon.ContextMenu = contextMenu1; _notifyIcon.ContextMenu = contextMenu1;
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
_notifyIcon.MouseClick += notifyIcon1_Click;
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
this.updateChecker = new UpdateChecker(); this.updateChecker = new UpdateChecker();
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted;
@@ -78,7 +85,7 @@ namespace Shadowsocks.View
if (config.autoCheckUpdate) if (config.autoCheckUpdate)
{ {
_isStartupChecking = true; _isStartupChecking = true;
updateChecker.CheckUpdate(config);
updateChecker.CheckUpdate(config, 3000);
} }
if (config.isDefault) if (config.isDefault)
@@ -200,7 +207,6 @@ namespace Shadowsocks.View
}); });
} }
private void controller_ConfigChanged(object sender, EventArgs e) private void controller_ConfigChanged(object sender, EventArgs e)
{ {
LoadCurrentConfiguration(); LoadCurrentConfiguration();
@@ -256,7 +262,6 @@ namespace Shadowsocks.View
if (updateChecker.NewVersionFound) if (updateChecker.NewVersionFound)
{ {
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000); ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000);
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
_isFirstRun = false; _isFirstRun = false;
} }
else if (!_isStartupChecking) else if (!_isStartupChecking)
@@ -269,11 +274,24 @@ namespace Shadowsocks.View
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
{ {
_notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked;
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\"";
System.Diagnostics.Process.Start("explorer.exe", argument);
if (updateChecker.NewVersionFound)
{
updateChecker.NewVersionFound = false; /* Reset the flag */
if (System.IO.File.Exists(updateChecker.LatestVersionLocalName))
{
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\"";
System.Diagnostics.Process.Start("explorer.exe", argument);
}
}
} }
private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e)
{
if (updateChecker.NewVersionFound)
{
updateChecker.NewVersionFound = false; /* Reset the flag */
}
}
private void LoadCurrentConfiguration() private void LoadCurrentConfiguration()
{ {
@@ -324,7 +342,6 @@ namespace Shadowsocks.View
{ {
item.Checked = true; item.Checked = true;
} }
} }
} }
@@ -342,10 +359,36 @@ namespace Shadowsocks.View
} }
} }
private void ShowLogForms()
{
if (logForms.Count == 0)
{
LogForm f = new LogForm(controller, Logging.LogFilePath);
f.Show();
f.FormClosed += logForm_FormClosed;
logForms.Add(f);
logFormsVisible = true;
}
else
{
logFormsVisible = !logFormsVisible;
foreach (LogForm f in logForms)
{
f.Visible = logFormsVisible;
}
}
}
void logForm_FormClosed(object sender, FormClosedEventArgs e)
{
logForms.Remove((LogForm)sender);
}
void configForm_FormClosed(object sender, FormClosedEventArgs e) void configForm_FormClosed(object sender, FormClosedEventArgs e)
{ {
configForm = null; configForm = null;
Util.Utils.ReleaseMemory(true);
Utils.ReleaseMemory(true);
ShowFirstTimeBalloon(); ShowFirstTimeBalloon();
} }
@@ -378,6 +421,18 @@ namespace Shadowsocks.View
Process.Start("https://github.com/shadowsocks/shadowsocks-windows"); Process.Start("https://github.com/shadowsocks/shadowsocks-windows");
} }
private void notifyIcon1_Click(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
// TODO: show something interesting
}
else if (e.Button == MouseButtons.Middle)
{
ShowLogForms();
}
}
private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e) private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e)
{ {
if (e.Button == MouseButtons.Left) if (e.Button == MouseButtons.Left)
@@ -436,11 +491,13 @@ namespace Shadowsocks.View
private void ShowLogItem_Click(object sender, EventArgs e) private void ShowLogItem_Click(object sender, EventArgs e)
{ {
string argument = Logging.LogFile;
LogForm f = new LogForm(controller, Logging.LogFilePath);
f.Show();
f.FormClosed += logForm_FormClosed;
new LogForm(controller, argument).Show();
logForms.Add(f);
} }
private void StatisticsConfigItem_Click(object sender, EventArgs e) private void StatisticsConfigItem_Click(object sender, EventArgs e)
{ {
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller); StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller);
@@ -549,9 +606,11 @@ namespace Shadowsocks.View
Process.Start(_urlToOpen); Process.Start(_urlToOpen);
} }
private void AutoStartupItem_Click(object sender, EventArgs e) {
private void AutoStartupItem_Click(object sender, EventArgs e)
{
AutoStartupItem.Checked = !AutoStartupItem.Checked; AutoStartupItem.Checked = !AutoStartupItem.Checked;
if (!AutoStartup.Set(AutoStartupItem.Checked)) {
if (!AutoStartup.Set(AutoStartupItem.Checked))
{
MessageBox.Show(I18N.GetString("Failed to update registry")); MessageBox.Show(I18N.GetString("Failed to update registry"));
} }
} }


+ 9
- 8
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs View File

@@ -2,16 +2,15 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Net.NetworkInformation;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using Shadowsocks.Controller; using Shadowsocks.Controller;
using Shadowsocks.Model; using Shadowsocks.Model;
using SimpleJson;
using System.Net.NetworkInformation;


namespace Shadowsocks.View namespace Shadowsocks.View
{ {
public partial class StatisticsStrategyConfigurationForm: Form
public partial class StatisticsStrategyConfigurationForm : Form
{ {
private readonly ShadowsocksController _controller; private readonly ShadowsocksController _controller;
private StatisticsStrategyConfiguration _configuration; private StatisticsStrategyConfiguration _configuration;
@@ -51,9 +50,9 @@ namespace Shadowsocks.View


serverSelector.DataSource = _servers; serverSelector.DataSource = _servers;


_dataTable.Columns.Add("Timestamp", typeof (DateTime));
_dataTable.Columns.Add("Package Loss", typeof (int));
_dataTable.Columns.Add("Ping", typeof (int));
_dataTable.Columns.Add("Timestamp", typeof(DateTime));
_dataTable.Columns.Add("Package Loss", typeof(int));
_dataTable.Columns.Add("Ping", typeof(int));


StatisticsChart.Series["Package Loss"].XValueMember = "Timestamp"; StatisticsChart.Series["Package Loss"].XValueMember = "Timestamp";
StatisticsChart.Series["Package Loss"].YValueMembers = "Package Loss"; StatisticsChart.Series["Package Loss"].YValueMembers = "Package Loss";
@@ -64,7 +63,6 @@ namespace Shadowsocks.View
StatisticsChart.DataBind(); StatisticsChart.DataBind();
} }



private void CancelButton_Click(object sender, EventArgs e) private void CancelButton_Click(object sender, EventArgs e)
{ {
Close(); Close();
@@ -85,6 +83,9 @@ namespace Shadowsocks.View
{ {
string serverName = _servers[serverSelector.SelectedIndex]; string serverName = _servers[serverSelector.SelectedIndex];
_dataTable.Rows.Clear(); _dataTable.Rows.Clear();

//return directly when no data is usable
if (_controller.availabilityStatistics?.FilteredStatistics == null) return;
List<AvailabilityStatistics.RawStatisticsData> statistics; List<AvailabilityStatistics.RawStatisticsData> statistics;
if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return; if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return;
IEnumerable<IGrouping<int, AvailabilityStatistics.RawStatisticsData>> dataGroups; IEnumerable<IGrouping<int, AvailabilityStatistics.RawStatisticsData>> dataGroups;


+ 2
- 2
shadowsocks-csharp/app.config View File

@@ -8,11 +8,11 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>


+ 10
- 8
shadowsocks-csharp/packages.config View File

@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net4-client" developmentDependency="true" />
<package id="Fody" version="1.29.3" targetFramework="net4-client" developmentDependency="true" />
<package id="Microsoft.Bcl" version="1.1.8" targetFramework="net4-client" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net4-client" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net4-client" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net4-client" />
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net4-client" developmentDependency="true" />
<package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net40-client" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net40-client" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net40-client" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net4-client" />
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net40-client" />
<package id="System.Net.Http" version="2.0.20710.0" targetFramework="net40-client" />
</packages> </packages>

+ 22
- 24
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -48,6 +48,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<Optimize>false</Optimize>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath> <OutputPath>bin\x86\Release\</OutputPath>
@@ -68,21 +69,18 @@
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> <HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
<Private>True</Private> <Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> <HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
<Private>True</Private> <Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference> </Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> <HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
<Private>True</Private> <Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference> </Reference>
<Reference Include="Microsoft.VisualBasic" /> <Reference Include="Microsoft.VisualBasic" />
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>3rd\Newtonsoft.Json.7.0.1\lib\net40\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>3rd\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
@@ -90,22 +88,26 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.IO, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.IO.dll</HintPath>
<Reference Include="System.IO, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath>
<Private>True</Private> <Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference> </Reference>
<Reference Include="System.Net" /> <Reference Include="System.Net" />
<Reference Include="System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="System.Runtime, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Runtime.dll</HintPath>
<Reference Include="System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll</HintPath>
<Private>True</Private> <Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference> </Reference>
<Reference Include="System.Threading.Tasks, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Threading.Tasks.dll</HintPath>
<Reference Include="System.Net.Http.WebRequest, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Threading.Tasks, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath>
<Private>True</Private> <Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference> </Reference>
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Forms.DataVisualization" /> <Reference Include="System.Windows.Forms.DataVisualization" />
@@ -162,7 +164,6 @@
<Compile Include="3rd\zxing\qrcode\encoder\MaskUtil.cs" /> <Compile Include="3rd\zxing\qrcode\encoder\MaskUtil.cs" />
<Compile Include="3rd\zxing\qrcode\encoder\MatrixUtil.cs" /> <Compile Include="3rd\zxing\qrcode\encoder\MatrixUtil.cs" />
<Compile Include="3rd\zxing\qrcode\encoder\QRCode.cs" /> <Compile Include="3rd\zxing\qrcode\encoder\QRCode.cs" />
<Compile Include="3rd\SimpleJson.cs" />
<Compile Include="3rd\zxing\qrcode\QRCodeReader.cs" /> <Compile Include="3rd\zxing\qrcode\QRCodeReader.cs" />
<Compile Include="3rd\zxing\Result.cs" /> <Compile Include="3rd\zxing\Result.cs" />
<Compile Include="3rd\zxing\ResultMetadataType.cs" /> <Compile Include="3rd\zxing\ResultMetadataType.cs" />
@@ -184,11 +185,11 @@
<Compile Include="Encryption\EncryptorBase.cs" /> <Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" /> <Compile Include="Encryption\EncryptorFactory.cs" />
<Compile Include="Encryption\IVEncryptor.cs" /> <Compile Include="Encryption\IVEncryptor.cs" />
<Compile Include="Encryption\MbedTLS.cs" />
<Compile Include="Encryption\PolarSSL.cs" /> <Compile Include="Encryption\PolarSSL.cs" />
<Compile Include="Encryption\PolarSSLEncryptor.cs" /> <Compile Include="Encryption\PolarSSLEncryptor.cs" />
<Compile Include="Encryption\Sodium.cs" /> <Compile Include="Encryption\Sodium.cs" />
<Compile Include="Encryption\SodiumEncryptor.cs" /> <Compile Include="Encryption\SodiumEncryptor.cs" />
<Compile Include="Encryption\TableEncryptor.cs" />
<Compile Include="Encryption\IEncryptor.cs" /> <Compile Include="Encryption\IEncryptor.cs" />
<Compile Include="Controller\Service\PACServer.cs" /> <Compile Include="Controller\Service\PACServer.cs" />
<Compile Include="Model\LogViewerConfig.cs" /> <Compile Include="Model\LogViewerConfig.cs" />
@@ -323,18 +324,15 @@
</BootstrapperPackage> </BootstrapperPackage>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
<Import Project="3rd\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('3rd\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('3rd\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets'))" />
<Error Condition="!Exists('3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
<Error Condition="!Exists('3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets'))" />
</Target> </Target>
<Import Project="3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Import Project="3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">


+ 15
- 0
test/UnitTest.cs View File

@@ -22,6 +22,21 @@ namespace test
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0); Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0);
} }
[TestMethod]
public void TestMD5()
{
for (int len = 1; len < 64; len++)
{
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
byte[] bytes = new byte[len];
var random = new Random();
random.NextBytes(bytes);
string md5str = Convert.ToBase64String(md5.ComputeHash(bytes));
string md5str2 = Convert.ToBase64String(MbedTLS.MD5(bytes));
Assert.IsTrue(md5str == md5str2);
}
}
private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor)
{ {
byte[] plain = new byte[16384]; byte[] plain = new byte[16384];


+ 8
- 0
test/packages.config View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="System.Net.Http" version="4.0.0" targetFramework="net45" />
</packages>

+ 35
- 0
test/test.csproj View File

@@ -17,6 +17,8 @@
<IsCodedUITest>False</IsCodedUITest> <IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType> <TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<OutputPath>bin\x86\Debug\</OutputPath> <OutputPath>bin\x86\Debug\</OutputPath>
@@ -30,7 +32,30 @@
<StartupObject /> <StartupObject />
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
</ItemGroup> </ItemGroup>
<Choose> <Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
@@ -54,6 +79,9 @@
<Name>shadowsocks-csharp</Name> <Name>shadowsocks-csharp</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Choose> <Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup> <ItemGroup>
@@ -74,6 +102,13 @@
</Choose> </Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">


Loading…
Cancel
Save