Browse Source

Merge pull request #1 from shadowsocks/master

Master
tags/3.0
NanaLich 9 years ago
parent
commit
6bb554bf8d
62 changed files with 3528 additions and 2934 deletions
  1. +7
    -0
      .gitignore
  2. +6
    -0
      CHANGES
  3. +2
    -2
      README.md
  4. +6
    -0
      nuget.config
  5. +0
    -1883
      shadowsocks-csharp/3rd/SimpleJson.cs
  6. +4
    -12
      shadowsocks-csharp/Controller/FileManager.cs
  7. +55
    -12
      shadowsocks-csharp/Controller/Logging.cs
  8. +272
    -48
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  9. +26
    -19
      shadowsocks-csharp/Controller/Service/GfwListUpdater.cs
  10. +7
    -9
      shadowsocks-csharp/Controller/Service/Listener.cs
  11. +93
    -40
      shadowsocks-csharp/Controller/Service/PACServer.cs
  12. +14
    -20
      shadowsocks-csharp/Controller/Service/PolipoRunner.cs
  13. +0
    -3
      shadowsocks-csharp/Controller/Service/PortForwarder.cs
  14. +96
    -84
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  15. +21
    -12
      shadowsocks-csharp/Controller/Service/UDPRelay.cs
  16. +183
    -77
      shadowsocks-csharp/Controller/Service/UpdateChecker.cs
  17. +102
    -23
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  18. +5
    -5
      shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs
  19. +0
    -176
      shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs
  20. +153
    -0
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
  21. +1
    -1
      shadowsocks-csharp/Controller/Strategy/StrategyManager.cs
  22. +37
    -7
      shadowsocks-csharp/Controller/System/AutoStartup.cs
  23. +13
    -5
      shadowsocks-csharp/Data/cn.txt
  24. BIN
      shadowsocks-csharp/Data/libsscrypto.dll.gz
  25. +3
    -3
      shadowsocks-csharp/Data/privoxy_conf.txt
  26. +6
    -2
      shadowsocks-csharp/Encryption/EncryptorBase.cs
  27. +4
    -8
      shadowsocks-csharp/Encryption/EncryptorFactory.cs
  28. +145
    -17
      shadowsocks-csharp/Encryption/IVEncryptor.cs
  29. +65
    -0
      shadowsocks-csharp/Encryption/MbedTLS.cs
  30. +7
    -10
      shadowsocks-csharp/Encryption/PolarSSL.cs
  31. +3
    -4
      shadowsocks-csharp/Encryption/PolarSSLEncryptor.cs
  32. +18
    -12
      shadowsocks-csharp/Encryption/Sodium.cs
  33. +7
    -2
      shadowsocks-csharp/Encryption/SodiumEncryptor.cs
  34. +0
    -106
      shadowsocks-csharp/Encryption/TableEncryptor.cs
  35. +5
    -0
      shadowsocks-csharp/FodyWeavers.xml
  36. +14
    -56
      shadowsocks-csharp/Model/Configuration.cs
  37. +103
    -0
      shadowsocks-csharp/Model/LogViewerConfig.cs
  38. +17
    -16
      shadowsocks-csharp/Model/Server.cs
  39. +106
    -0
      shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs
  40. +14
    -10
      shadowsocks-csharp/Program.cs
  41. +10
    -0
      shadowsocks-csharp/Properties/DataSources/Shadowsocks.Model.StatisticsStrategyConfiguration.datasource
  42. +38
    -40
      shadowsocks-csharp/Properties/Resources.Designer.cs
  43. +56
    -13
      shadowsocks-csharp/Util/Util.cs
  44. +111
    -0
      shadowsocks-csharp/View/CalculationControl.Designer.cs
  45. +25
    -0
      shadowsocks-csharp/View/CalculationControl.cs
  46. +120
    -0
      shadowsocks-csharp/View/CalculationControl.resx
  47. +72
    -58
      shadowsocks-csharp/View/ConfigForm.Designer.cs
  48. +44
    -3
      shadowsocks-csharp/View/ConfigForm.cs
  49. +116
    -54
      shadowsocks-csharp/View/LogForm.Designer.cs
  50. +179
    -28
      shadowsocks-csharp/View/LogForm.cs
  51. +24
    -3
      shadowsocks-csharp/View/LogForm.resx
  52. +130
    -29
      shadowsocks-csharp/View/MenuViewController.cs
  53. +531
    -0
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs
  54. +141
    -0
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs
  55. +126
    -0
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx
  56. +17
    -4
      shadowsocks-csharp/app.config
  57. +1
    -1
      shadowsocks-csharp/app.manifest
  58. +11
    -0
      shadowsocks-csharp/packages.config
  59. +84
    -3
      shadowsocks-csharp/shadowsocks-csharp.csproj
  60. +29
    -14
      test/UnitTest.cs
  61. +8
    -0
      test/packages.config
  62. +35
    -0
      test/test.csproj

+ 7
- 0
.gitignore View File

@@ -1,3 +1,4 @@
/.vs/
Backup/
bin/
obj/
@@ -5,3 +6,9 @@ shadowsocks-csharp/shadowsocks-csharp.csproj.user
TestResults
*.suo

shadowsocks-csharp/3rd/*
!shadowsocks-csharp/3rd/zxing/
!shadowsocks-csharp/3rd/SimpleJson.cs
packages/*

shadowsocks-csharp.sln.DotSettings.user

+ 6
- 0
CHANGES View File

@@ -1,3 +1,9 @@
2.5.8 2015-09-20
- Update GFWList url

2.5.7 2015-09-19
- Fix repeated IV

2.5.6 2015-08-19
- Add portable mode. Create shadowsocks_portable_mode.txt to use it
- Support server reorder


+ 2
- 2
README.md View File

@@ -75,8 +75,8 @@ Visual Studio 2015 is required.
GPLv3


[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master
[Appveyor]: https://ci.appveyor.com/project/icylogic/shadowsocks-windows-l9mwe
[Build Status]: https://ci.appveyor.com/api/projects/status/ytllr9yjkbpc2tu2/branch/master
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side


+ 6
- 0
nuget.config View File

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

+ 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.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace Shadowsocks.Controller
{
@@ -12,9 +10,7 @@ namespace Shadowsocks.Controller
{
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.Close();
return true;
@@ -31,7 +27,7 @@ namespace Shadowsocks.Controller
{
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.
byte[] buffer = new byte[4096];
int n;
@@ -39,17 +35,13 @@ namespace Shadowsocks.Controller
using (GZipStream input = new GZipStream(new MemoryStream(content),
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.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.Net.Sockets;
using System.Text;
using System.Net;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
public class Logging
{
public static string LogFile;
public static string LogFilePath;
public static bool OpenLogFile()
{
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);
sw.AutoFlush = true;
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)
{
#if DEBUG
WriteToLogFile("[D] " + o);
#endif
}
public static void Debug(EndPoint local, EndPoint remote, int len, string header = null, string tailer = null)
{
#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
}
@@ -57,11 +92,19 @@ namespace Shadowsocks.Controller
}
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
{
Console.WriteLine(e);
Info(e);
}
}
else if (e is ObjectDisposedException)
@@ -69,7 +112,7 @@ namespace Shadowsocks.Controller
}
else
{
Console.WriteLine(e);
Info(e);
}
}
}


+ 272
- 48
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -1,50 +1,75 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

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

using Shadowsocks.Model;
using System.Reflection;
using Shadowsocks.Util;
using Shadowsocks.Util;

namespace Shadowsocks.Controller
{
class AvailabilityStatistics
using DataUnit = KeyValuePair<string, string>;
using DataList = List<KeyValuePair<string, string>>;

using Statistics = Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>>;

public class AvailabilityStatistics
{
private static readonly string StatisticsFilesName = "shadowsocks.availability.csv";
private static readonly string Delimiter = ",";
private static readonly int Timeout = 500;
private static readonly int Repeat = 4; //repeat times every evaluation
private static readonly int Interval = 10 * 60 * 1000; //evaluate proxies every 15 minutes
private Timer timer = null;
private State state = null;
private List<Server> servers;
public static readonly string DateTimePattern = "yyyy-MM-dd HH:mm:ss";
private const string StatisticsFilesName = "shadowsocks.availability.csv";
private const string Delimiter = ",";
private const int Timeout = 500;
private const int DelayBeforeStart = 1000;
public Statistics RawStatistics { get; private set; }
public Statistics FilteredStatistics { get; private set; }
public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1);
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 Timer _timer;
private State _state;
private List<Server> _servers;
private StatisticsStrategyConfiguration _config;

public static string AvailabilityStatisticsFile;

//static constructor to initialize every public static fields before refereced
static AvailabilityStatistics()
{
string temppath = Utils.GetTempPath();
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName);
AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName);
}

public bool Set(bool enabled)
public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig)
{
UpdateConfiguration(config, statisticsConfig);
}

public bool Set(StatisticsStrategyConfiguration config)
{
_config = config;
try
{
if (enabled)
if (config.StatisticsEnabled)
{
if (timer?.Change(0, Interval) == null)
if (_timer?.Change(DelayBeforeStart, Interval) == null)
{
state = new State();
timer = new Timer(Evaluate, state, 0, Interval);
_state = new State();
_timer = new Timer(Run, _state, DelayBeforeStart, Interval);
}
}
else
{
timer?.Dispose();
_timer?.Dispose();
}
return true;
}
@@ -55,63 +80,262 @@ namespace Shadowsocks.Controller
}
}

private void Evaluate(object obj)
//hardcode
//TODO: backup reliable isp&geolocation provider or a local database is required
public static async Task<DataList> GetGeolocationAndIsp()
{
Ping ping = new Ping();
State state = (State) obj;
foreach (var server in servers)
Logging.Debug("Retrive information of geolocation and isp");
const string API = "http://ip-api.com/json";
const string alternativeAPI = "http://www.telize.com/geoip"; //must be comptible with current API
var result = await GetInfoFromAPI(API);
if (result != null) return result;
result = await GetInfoFromAPI(alternativeAPI);
if (result != null) return result;
return new DataList
{
new DataUnit(State.Geolocation, State.Unknown),
new DataUnit(State.ISP, State.Unknown)
};
}

private static async Task<DataList> GetInfoFromAPI(string API)
{
string jsonString;
try
{
jsonString = await new HttpClient().GetStringAsync(API);
}
catch (HttpRequestException e)
{
Logging.LogUsefulException(e);
return null;
}
JObject obj;
try
{
obj = JObject.Parse(jsonString);
}
catch (JsonReaderException)
{
Logging.Debug("eveluating " + server.FriendlyName());
foreach (var _ in Enumerable.Range(0, Repeat))
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;
return new DataList {
new DataUnit(State.Geolocation, $"\"{country} {city}\""),
new DataUnit(State.ISP, $"\"{isp}\"")
};
}

private async Task<List<DataList>> ICMPTest(Server server)
{
Logging.Debug("Ping " + server.FriendlyName());
if (server.server == "") return null;
var ret = new List<DataList>();
try {
var IP = Dns.GetHostAddresses(server.server).First(ip => (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6));
var ping = new Ping();

foreach (var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString(DateTimePattern)))
{
//TODO: do simple analyze of data to provide friendly message, like package loss.
string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
//ICMP echo. we can also set options and special bytes
//seems no need to use SendPingAsync
try
{
PingReply reply = ping.Send(server.server, Timeout);
state.data = new List<KeyValuePair<string, string>>();
state.data.Add(new KeyValuePair<string, string>("Timestamp", timestamp));
state.data.Add(new KeyValuePair<string, string>("Server", server.FriendlyName()));
state.data.Add(new KeyValuePair<string, string>("Status", reply.Status.ToString()));
state.data.Add(new KeyValuePair<string, string>("RoundtripTime", reply.RoundtripTime.ToString()));
//state.data.Add(new KeyValuePair<string, string>("data", reply.Buffer.ToString())); // The data of reply
Append(state.data);
var reply = await ping.SendTaskAsync(IP, Timeout);
ret.Add(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Timestamp", timestamp),
new KeyValuePair<string, string>("Server", server.FriendlyName()),
new KeyValuePair<string, string>("Status", reply?.Status.ToString()),
new KeyValuePair<string, string>("RoundtripTime", reply?.RoundtripTime.ToString())
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply
});
Thread.Sleep(Timeout + new Random().Next() % Timeout);
//Do ICMPTest in a random frequency
}
catch (Exception e)
{
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}");
Logging.LogUsefulException(e);
}
}
}catch(Exception e)
{
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}");
Logging.LogUsefulException(e);
}
return ret;
}

private void Run(object obj)
{
LoadRawStatistics();
FilterRawStatistics();
evaluate();
}

private async void evaluate()
{
var geolocationAndIsp = GetGeolocationAndIsp();
foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest)))
{
if (dataLists == null) continue;
foreach (var dataList in dataLists.Where(dataList => dataList != null))
{
await geolocationAndIsp;
Append(dataList, geolocationAndIsp.Result);
}
}
}

private static void Append(List<KeyValuePair<string, string>> data)
private static void Append(DataList dataList, IEnumerable<DataUnit> extra)
{
string dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray());
var data = dataList.Concat(extra);
var dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray());
string[] lines;
if (!File.Exists(AvailabilityStatisticsFile))
{
string headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray());
lines = new string[] { headerLine, dataLine };
var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray());
lines = new[] { headerLine, dataLine };
}
else
{
lines = new string[] { dataLine };
lines = new[] { dataLine };
}
File.AppendAllLines(AvailabilityStatisticsFile, lines);
try
{
File.AppendAllLines(AvailabilityStatisticsFile, lines);
}
catch (IOException e)
{
Logging.LogUsefulException(e);
}
}

internal void UpdateConfiguration(Configuration config, StatisticsStrategyConfiguration statisticsConfig)
{
Set(statisticsConfig);
_servers = config.configs;
}

private async void FilterRawStatistics()
{
if (RawStatistics == null) return;
if (FilteredStatistics == null)
{
FilteredStatistics = new Statistics();
}
foreach (IEnumerable<RawStatisticsData> rawData in RawStatistics.Values)
{
var filteredData = rawData;
if (_config.ByIsp)
{
var current = await GetGeolocationAndIsp();
filteredData =
filteredData.Where(
data =>
data.Geolocation == current[0].Value ||
data.Geolocation == State.Unknown);
filteredData =
filteredData.Where(
data => data.ISP == current[1].Value || data.ISP == State.Unknown);
if (filteredData.LongCount() == 0) return;
}
if (_config.ByHourOfDay)
{
var currentHour = DateTime.Now.Hour;
filteredData = filteredData.Where(data =>
data.Timestamp != UnknownDateTime && data.Timestamp.Hour.Equals(currentHour)
);
if (filteredData.LongCount() == 0) return;
}
var dataList = filteredData as List<RawStatisticsData> ?? filteredData.ToList();
var serverName = dataList[0].ServerName;
FilteredStatistics[serverName] = dataList;
}
}

private void LoadRawStatistics()
{
try
{
var path = AvailabilityStatisticsFile;
Logging.Debug($"loading statistics from {path}");
if (!File.Exists(path))
{
try {
using (FileStream fs = File.Create(path))
{
//do nothing
}
}catch(Exception e)
{
Logging.LogUsefulException(e);
}
if (!File.Exists(path)) {
Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval / 60 / 1000} minutes later");
_timer.Change(RetryInterval, Interval);
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);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}

private DateTime ParseExactOrUnknown(string str)
{
DateTime dateTime;
return !DateTime.TryParseExact(str, DateTimePattern, null, DateTimeStyles.None, out dateTime) ? UnknownDateTime : dateTime;
}

public class State
{
public DataList dataList = new DataList();
public const string Geolocation = "Geolocation";
public const string ISP = "ISP";
public const string Unknown = "Unknown";
}

internal void UpdateConfiguration(Configuration _config)
public class RawStatisticsData
{
Set(_config.availabilityStatistics);
servers = _config.configs;
public DateTime Timestamp;
public string ServerName;
public string ICMPStatus;
public int RoundtripTime;
public string Geolocation;
public string ISP;
}

private class State
public class StatisticsData
{
public List<KeyValuePair<string, string>> data = new List<KeyValuePair<string, string>>();
public float PackageLoss;
public int AverageResponse;
public int MinResponse;
public int MaxResponse;
}
}
}

+ 26
- 19
shadowsocks-csharp/Controller/Service/GfwListUpdater.cs View File

@@ -1,22 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Net;
using System.Text;
using Newtonsoft.Json;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using SimpleJson;
using Shadowsocks.Util;
using Shadowsocks.Model;
namespace Shadowsocks.Controller
{
public class GFWListUpdater
{
private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt";
private static string PAC_FILE = PACServer.PAC_FILE;
private static string USER_RULE_FILE = PACServer.USER_RULE_FILE;
private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt";
public event EventHandler<ResultEventArgs> UpdateCompleted;
@@ -36,30 +34,39 @@ namespace Shadowsocks.Controller
{
try
{
File.WriteAllText(Utils.GetTempPath("gfwlist.txt"), e.Result, Encoding.UTF8);
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);
foreach(string rule in rules)
foreach (string rule in rules)
{
if (rule.StartsWith("!") || rule.StartsWith("["))
continue;
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)
{
UpdateCompleted(this, new ResultEventArgs(false));
return;
}
}
File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8);
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8);
if (UpdateCompleted != null)
{
UpdateCompleted(this, new ResultEventArgs(true));
@@ -82,7 +89,7 @@ namespace Shadowsocks.Controller
http.DownloadStringAsync(new Uri(GFWLIST_URL));
}
public List<string> ParseResult(string response)
public static List<string> ParseResult(string response)
{
byte[] bytes = Convert.FromBase64String(response);
string content = Encoding.ASCII.GetString(bytes);
@@ -97,4 +104,4 @@ namespace Shadowsocks.Controller
return valid_lines;
}
}
}
}

+ 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.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using Shadowsocks.Model;
namespace Shadowsocks.Controller
{
@@ -78,10 +78,8 @@ namespace Shadowsocks.Controller
_tcpSocket.Listen(1024);
// 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();
_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)
{
Console.WriteLine(e);
Logging.LogUsefulException(e);
}
finally
{
@@ -208,7 +206,7 @@ namespace Shadowsocks.Controller
}
catch (Exception e)
{
Console.WriteLine(e);
Logging.LogUsefulException(e);
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.Compression;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
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;
public event EventHandler PACFileChanged;
public event EventHandler UserRuleFileChanged;
public PACServer()
{
this.WatchPacFile();
this.WatchUserRuleFile();
}
public void UpdateConfiguration(Configuration config)
@@ -46,7 +48,7 @@ namespace Shadowsocks.Controller
bool hostMatch = false, pathMatch = false, useSocks = false;
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[0] == "Host")
@@ -56,14 +58,14 @@ namespace Shadowsocks.Controller
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)
{
@@ -142,14 +144,14 @@ Content-Type: application/x-ns-proxy-autoconfig
Content-Length: {0}
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);
Util.Utils.ReleaseMemory(true);
Utils.ReleaseMemory(true);
}
catch (Exception e)
{
Console.WriteLine(e);
Logging.LogUsefulException(e);
socket.Close();
}
}
@@ -167,27 +169,78 @@ Connection: Close
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)
{
@@ -202,7 +255,7 @@ Connection: Close
//}
//catch (Exception e)
//{
// Console.WriteLine(e);
// Logging.LogUsefulException(e);
//}
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.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Net.NetworkInformation;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Text;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
@@ -16,16 +16,14 @@ namespace Shadowsocks.Controller
class PolipoRunner
{
private Process _process;
private static string temppath;
private int _runningPort;
static PolipoRunner()
{
temppath = Utils.GetTempPath();
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)
{
@@ -56,7 +54,7 @@ namespace Shadowsocks.Controller
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Logging.LogUsefulException(e);
}
}
string polipoConfig = Resources.privoxy_conf;
@@ -64,20 +62,16 @@ namespace Shadowsocks.Controller
polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.ToString());
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(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();
// 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.UseShellExecute = true;
_process.StartInfo.CreateNoWindow = true;
//_process.StartInfo.RedirectStandardOutput = true;
//_process.StartInfo.RedirectStandardError = true;
_process.Start();
}
RefreshTrayArea();
@@ -94,7 +88,7 @@ namespace Shadowsocks.Controller
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Logging.LogUsefulException(e);
}
_process = null;
}


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

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


+ 96
- 84
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -1,31 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Net.Sockets;
using System.Timers;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
using Shadowsocks.Controller.Strategy;
using System.Timers;
namespace Shadowsocks.Controller
{
class TCPRelay : Listener.Service
{
private ShadowsocksController _controller;
private DateTime _lastSweepTime;
public ISet<Handler> Handlers
public ISet<TCPHandler> Handlers
{
get; set;
}
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)
@@ -39,22 +38,21 @@ namespace Shadowsocks.Controller
return false;
}
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
Handler handler = new Handler();
TCPHandler handler = new TCPHandler(this);
handler.connection = socket;
handler.controller = _controller;
handler.relay = this;
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;
if (now - _lastSweepTime > TimeSpan.FromSeconds(1))
{
_lastSweepTime = now;
foreach (Handler handler1 in this.Handlers)
foreach (TCPHandler handler1 in Handlers)
{
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();
}
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 Server server;
// Client socket.
@@ -85,6 +93,7 @@ namespace Shadowsocks.Controller
public DateTime lastActivity;
private const int maxRetry = 4;
private int retryCount = 0;
private bool connected;
@@ -93,17 +102,18 @@ namespace Shadowsocks.Controller
private int _firstPacketLength;
// Size of receive buffer.
public const int RecvSize = 8192;
public const int BufferSize = RecvSize + 32;
public const int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth
public const int BufferSize = RecvSize + RecvReserveSize + 32;
private int totalRead = 0;
private int totalWrite = 0;
// remote receive buffer
private byte[] remoteRecvBuffer = new byte[RecvSize];
private byte[] remoteRecvBuffer = new byte[BufferSize];
// remote send buffer
private byte[] remoteSendBuffer = new byte[BufferSize];
// connection receive buffer
private byte[] connetionRecvBuffer = new byte[RecvSize];
private byte[] connetionRecvBuffer = new byte[BufferSize];
// connection send buffer
private byte[] connetionSendBuffer = new byte[BufferSize];
// Received data string.
@@ -116,6 +126,12 @@ namespace Shadowsocks.Controller
private object decryptionLock = new object();
private DateTime _startConnectTime;
private TCPRelay tcprelay; // TODO: tcprelay ?= relay
public TCPHandler(TCPRelay tcprelay)
{
this.tcprelay = tcprelay;
}
public void CreateRemote()
{
@@ -124,23 +140,23 @@ namespace Shadowsocks.Controller
{
throw new ArgumentException("No server configured");
}
this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password);
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
this.server = server;
}
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()
{
if (connectionShutdown && remoteShutdown)
{
this.Close();
Close();
}
}
@@ -148,7 +164,6 @@ namespace Shadowsocks.Controller
{
lock (relay.Handlers)
{
Logging.Debug($"connections: {relay.Handlers.Count}");
relay.Handlers.Remove(this);
}
lock (this)
@@ -212,19 +227,19 @@ namespace Shadowsocks.Controller
{
// reject socks 4
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);
}
else
{
this.Close();
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -238,20 +253,19 @@ namespace Shadowsocks.Controller
{
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
// 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)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -280,14 +294,14 @@ namespace Shadowsocks.Controller
}
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)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -323,27 +337,27 @@ namespace Shadowsocks.Controller
if (ar.AsyncState != null)
{
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
{
int bytesRead = connection.EndReceive(ar);
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
{
this.Close();
Close();
}
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -359,15 +373,16 @@ namespace Shadowsocks.Controller
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
// inner class
private class ServerTimer : Timer
{
public Server Server;
public ServerTimer(int p) :base(p)
public ServerTimer(int p) : base(p)
{
}
}
@@ -401,13 +416,12 @@ namespace Shadowsocks.Controller
connected = false;
// Connect to the remote endpoint.
remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), connectTimer);
remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -423,22 +437,22 @@ namespace Shadowsocks.Controller
{
strategy.SetFailure(server);
}
Console.WriteLine(String.Format("{0} timed out", server.FriendlyName()));
Logging.Info($"{server.FriendlyName()} timed out");
remote.Close();
RetryConnect();
}
private void RetryConnect()
{
if (retryCount < 4)
if (retryCount < maxRetry)
{
Logging.Debug("Connection failed, retrying");
Logging.Debug($"Connection failed, retry ({retryCount})");
StartConnect();
retryCount++;
}
else
{
this.Close();
Close();
}
}
@@ -462,8 +476,7 @@ namespace Shadowsocks.Controller
connected = true;
//Console.WriteLine("Socket connected to {0}",
// remote.RemoteEndPoint.ToString());
Logging.Debug($"Socket connected to {remote.RemoteEndPoint}");
var latency = DateTime.Now - _startConnectTime;
IStrategy strategy = controller.GetCurrentStrategy();
@@ -500,15 +513,13 @@ namespace Shadowsocks.Controller
}
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)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -522,10 +533,11 @@ namespace Shadowsocks.Controller
{
int bytesRead = remote.EndReceive(ar);
totalRead += bytesRead;
tcprelay.UpdateInboundCounter(bytesRead);
if (bytesRead > 0)
{
this.lastActivity = DateTime.Now;
lastActivity = DateTime.Now;
int bytesToSend;
lock (decryptionLock)
{
@@ -535,33 +547,33 @@ namespace Shadowsocks.Controller
}
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);
IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null)
{
strategy.UpdateLastRead(this.server);
strategy.UpdateLastRead(server);
}
}
else
{
//Console.WriteLine("bytesRead: " + bytesRead.ToString());
connection.Shutdown(SocketShutdown.Send);
connectionShutdown = true;
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)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -587,12 +599,14 @@ namespace Shadowsocks.Controller
}
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);
IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null)
{
strategy.UpdateLastWrite(this.server);
strategy.UpdateLastWrite(server);
}
}
else
@@ -605,7 +619,7 @@ namespace Shadowsocks.Controller
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -618,13 +632,12 @@ namespace Shadowsocks.Controller
try
{
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)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -637,13 +650,12 @@ namespace Shadowsocks.Controller
try
{
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)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
}


+ 21
- 12
shadowsocks-csharp/Controller/Service/UDPRelay.cs View File

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


+ 183
- 77
shadowsocks-csharp/Controller/Service/UpdateChecker.cs View File

@@ -1,92 +1,73 @@
using Shadowsocks.Model;
using System;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using SimpleJson;
using Newtonsoft.Json.Linq;
using Shadowsocks.Model;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
public class UpdateChecker
{
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases";
private const string UserAgent = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36";
private Configuration config;
public bool NewVersionFound;
public string LatestVersionNumber;
public string LatestVersionName;
public string LatestVersionURL;
public event EventHandler NewVersionFound;
public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted;
public const string Version = "2.5.6";
public const string Version = "2.5.8.2";
public void CheckUpdate(Configuration config)
private class CheckUpdateTimer : System.Timers.Timer
{
// TODO test failures
WebClient http = new WebClient();
http.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36");
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort);
http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL));
}
public Configuration config;
public static int CompareVersion(string l, string r)
{
var ls = l.Split('.');
var rs = r.Split('.');
for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++)
public CheckUpdateTimer(int p) : base(p)
{
int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0;
int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0;
if (lp != rp)
{
return lp - rp;
}
}
return 0;
}
public class VersionComparer : IComparer<string>
public void CheckUpdate(Configuration config, int delay)
{
// Calls CaseInsensitiveComparer.Compare with the parameters reversed.
public int Compare(string x, string y)
{
return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y));
}
CheckUpdateTimer timer = new CheckUpdateTimer(delay);
timer.AutoReset = false;
timer.Elapsed += Timer_Elapsed;
timer.config = config;
timer.Enabled = true;
}
private static string ParseVersionFromURL(string url)
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase);
if (match.Success)
{
if (match.Groups.Count == 2)
{
return match.Groups[1].Value;
}
}
return null;
CheckUpdateTimer timer = (CheckUpdateTimer)sender;
Configuration config = timer.config;
timer.Elapsed -= Timer_Elapsed;
timer.Enabled = false;
timer.Dispose();
CheckUpdate(config);
}
private void SortVersions(List<string> versions)
public void CheckUpdate(Configuration config)
{
versions.Sort(new VersionComparer());
}
this.config = config;
private bool IsNewVersion(string url)
{
if (url.IndexOf("prerelease") >= 0)
try
{
return false;
Logging.Debug("Checking updates...");
WebClient http = CreateWebClient();
http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL));
}
string version = ParseVersionFromURL(url);
if (version == null)
catch (Exception ex)
{
return false;
Logging.LogUsefulException(ex);
}
string currentVersion = Version;
return CompareVersion(version, currentVersion) > 0;
}
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
@@ -95,42 +76,167 @@ namespace Shadowsocks.Controller
{
string response = e.Result;
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
JArray result = JArray.Parse(response);
List<string> versions = new List<string>();
foreach (JsonObject release in result)
List<Asset> asserts = new List<Asset>();
if (result != null)
{
if ((bool)release["prerelease"])
{
continue;
}
foreach (JsonObject asset in (JsonArray)release["assets"])
foreach (JObject release in result)
{
string url = (string)asset["browser_download_url"];
if (IsNewVersion(url))
if ((bool)release["prerelease"])
{
continue;
}
foreach (JObject asset in (JArray)release["assets"])
{
versions.Add(url);
Asset ass = new Asset();
ass.Parse(asset);
if (ass.IsNewVersion(Version))
{
asserts.Add(ass);
}
}
}
}
if (asserts.Count != 0)
{
SortByVersions(asserts);
Asset asset = asserts[asserts.Count - 1];
NewVersionFound = true;
LatestVersionURL = asset.browser_download_url;
LatestVersionNumber = asset.version;
LatestVersionName = asset.name;
if (versions.Count == 0)
startDownload();
}
else
{
Logging.Debug("No update is available");
if (CheckUpdateCompleted != null)
{
CheckUpdateCompleted(this, new EventArgs());
}
}
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
}
private void startDownload()
{
try
{
LatestVersionLocalName = Utils.GetTempPath(LatestVersionName);
WebClient http = CreateWebClient();
http.DownloadFileCompleted += Http_DownloadFileCompleted;
http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName);
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
}
private void Http_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
try
{
if (e.Error != null)
{
Logging.LogUsefulException(e.Error);
return;
}
// sort versions
SortVersions(versions);
LatestVersionURL = versions[versions.Count - 1];
LatestVersionNumber = ParseVersionFromURL(LatestVersionURL);
if (NewVersionFound != null)
Logging.Debug($"New version {LatestVersionNumber} found: {LatestVersionLocalName}");
if (CheckUpdateCompleted != null)
{
NewVersionFound(this, new EventArgs());
CheckUpdateCompleted(this, new EventArgs());
}
}
catch (Exception ex)
{
Logging.Debug(ex.ToString());
return;
Logging.LogUsefulException(ex);
}
}
private WebClient CreateWebClient()
{
WebClient http = new WebClient();
http.Headers.Add("User-Agent", UserAgent);
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort);
return http;
}
private void SortByVersions(List<Asset> asserts)
{
asserts.Sort(new VersionComparer());
}
public class Asset
{
public bool prerelease;
public string name;
public string version;
public string browser_download_url;
public bool IsNewVersion(string currentVersion)
{
if (prerelease)
{
return false;
}
if (version == null)
{
return false;
}
return CompareVersion(version, currentVersion) > 0;
}
public void Parse(JObject asset)
{
name = (string)asset["name"];
browser_download_url = (string)asset["browser_download_url"];
version = ParseVersionFromURL(browser_download_url);
prerelease = browser_download_url.IndexOf("prerelease") >= 0;
}
private static string ParseVersionFromURL(string url)
{
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase);
if (match.Success)
{
if (match.Groups.Count == 2)
{
return match.Groups[1].Value;
}
}
return null;
}
public static int CompareVersion(string l, string r)
{
var ls = l.Split('.');
var rs = r.Split('.');
for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++)
{
int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0;
int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0;
if (lp != rp)
{
return lp - rp;
}
}
return 0;
}
}
class VersionComparer : IComparer<Asset>
{
// Calls CaseInsensitiveComparer.Compare with the parameters reversed.
public int Compare(Asset x, Asset y)
{
return Asset.CompareVersion(x.version, y.version);
}
}
}


+ 102
- 23
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.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using Newtonsoft.Json;
using Shadowsocks.Controller.Strategy;
using System.Net;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
@@ -25,7 +30,12 @@ namespace Shadowsocks.Controller
private StrategyManager _strategyManager;
private PolipoRunner polipoRunner;
private GFWListUpdater gfwListUpdater;
private AvailabilityStatistics _availabilityStatics;
public AvailabilityStatistics availabilityStatistics { get; private set; }
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }
public long inboundCounter = 0;
public long outboundCounter = 0;
private bool stopped = false;
private bool _systemProxyIsDirty = false;
@@ -53,6 +63,7 @@ namespace Shadowsocks.Controller
public ShadowsocksController()
{
_config = Configuration.Load();
StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
_strategyManager = new StrategyManager(this);
StartReleasingMemory();
}
@@ -122,7 +133,13 @@ namespace Shadowsocks.Controller
{
_config.configs = servers;
_config.localPort = localPort;
SaveConfig(_config);
Configuration.Save(_config);
}
public void SaveStrategyConfigurations(StatisticsStrategyConfiguration configuration)
{
StatisticsConfiguration = configuration;
StatisticsStrategyConfiguration.Save(configuration);
}
public bool AddServerBySSURL(string ssURL)
@@ -236,7 +253,7 @@ namespace Shadowsocks.Controller
public static string GetQRCode(Server server)
{
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;
}
@@ -248,14 +265,12 @@ namespace Shadowsocks.Controller
}
}
public void ToggleAvailabilityStatistics(bool enabled)
public void UpdateStatisticsConfiguration(bool enabled)
{
if (_availabilityStatics != null)
{
_availabilityStatics.Set(enabled);
_config.availabilityStatistics = enabled;
SaveConfig(_config);
}
if (availabilityStatistics == null) return;
availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration);
_config.availabilityStatistics = enabled;
SaveConfig(_config);
}
public void SavePACUrl(string pacUrl)
@@ -280,10 +295,33 @@ namespace Shadowsocks.Controller
}
}
public void ToggleCheckingUpdate(bool enabled)
{
_config.autoCheckUpdate = enabled;
Configuration.Save(_config);
}
public void SaveLogViewerConfig(LogViewerConfig newConfig)
{
_config.logViewer = newConfig;
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()
{
// some logic in configuration updated the config when saving, we need to read it again
_config = Configuration.Load();
StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
if (polipoRunner == null)
{
@@ -293,6 +331,7 @@ namespace Shadowsocks.Controller
{
_pacServer = new PACServer();
_pacServer.PACFileChanged += pacServer_PACFileChanged;
_pacServer.UserRuleFileChanged += pacServer_UserRuleFileChanged;
}
_pacServer.UpdateConfiguration(_config);
if (gfwListUpdater == null)
@@ -302,17 +341,16 @@ namespace Shadowsocks.Controller
gfwListUpdater.Error += pacServer_PACUpdateError;
}
if (_listener != null)
if (availabilityStatistics == null)
{
_listener.Stop();
availabilityStatistics = new AvailabilityStatistics(_config, StatisticsConfiguration);
}
availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration);
if (_availabilityStatics == null)
if (_listener != null)
{
_availabilityStatics = new AvailabilityStatistics();
_availabilityStatics.UpdateConfiguration(_config);
_listener.Stop();
}
// don't put polipoRunner.Start() before pacServer.Stop()
// or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1
// though UseShellExecute is set to true now
@@ -360,7 +398,7 @@ namespace Shadowsocks.Controller
}
UpdateSystemProxy();
Util.Utils.ReleaseMemory(true);
Utils.ReleaseMemory(true);
}
protected void SaveConfig(Configuration newConfig)
@@ -404,6 +442,47 @@ namespace Shadowsocks.Controller
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()
{
_ramThread = new Thread(new ThreadStart(ReleaseMemory));
@@ -415,7 +494,7 @@ namespace Shadowsocks.Controller
{
while (true)
{
Util.Utils.ReleaseMemory(false);
Utils.ReleaseMemory(false);
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)
{
_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)
{
Logging.Debug(String.Format("latency: {0} {1}", server.FriendlyName(), latency));
Logging.Debug($"latency: {server.FriendlyName()} {latency}");
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
@@ -151,7 +151,7 @@ namespace Shadowsocks.Controller.Strategy
public void UpdateLastRead(Model.Server server)
{
Logging.Debug(String.Format("last read: {0}", server.FriendlyName()));
Logging.Debug($"last read: {server.FriendlyName()}");
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
@@ -162,7 +162,7 @@ namespace Shadowsocks.Controller.Strategy
public void UpdateLastWrite(Model.Server server)
{
Logging.Debug(String.Format("last write: {0}", server.FriendlyName()));
Logging.Debug($"last write: {server.FriendlyName()}");
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
@@ -173,7 +173,7 @@ namespace Shadowsocks.Controller.Strategy
public void SetFailure(Model.Server server)
{
Logging.Debug(String.Format("failure: {0}", server.FriendlyName()));
Logging.Debug($"failure: {server.FriendlyName()}");
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))


+ 0
- 176
shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs View File

@@ -1,176 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using Shadowsocks.Model;
using System.IO;
using System.Net.NetworkInformation;
using System.Threading;

namespace Shadowsocks.Controller.Strategy
{
class SimplyChooseByStatisticsStrategy : IStrategy
{
private ShadowsocksController _controller;
private Server _currentServer;
private Timer timer;
private Dictionary<string, StatisticsData> statistics;
private static readonly int CachedInterval = 30 * 60 * 1000; //choose a new server every 30 minutes

public SimplyChooseByStatisticsStrategy(ShadowsocksController controller)
{
_controller = controller;
var servers = controller.GetCurrentConfiguration().configs;
int randomIndex = new Random().Next() % servers.Count();
_currentServer = servers[randomIndex]; //choose a server randomly at first
timer = new Timer(ReloadStatisticsAndChooseAServer);
}

private void ReloadStatisticsAndChooseAServer(object obj)
{
Logging.Debug("Reloading statistics and choose a new server....");
List<Server> servers = _controller.GetCurrentConfiguration().configs;
LoadStatistics();
ChooseNewServer(servers);
}

/*
return a dict:
{
'ServerFriendlyName1':StatisticsData,
'ServerFriendlyName2':...
}
*/
private void LoadStatistics()
{
try
{
var path = AvailabilityStatistics.AvailabilityStatisticsFile;
Logging.Debug(string.Format("loading statistics from{0}", path));
statistics = (from l in File.ReadAllLines(path)
.Skip(1)
let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
let rawData = new
{
ServerName = strings[1],
IPStatus = strings[2],
RoundtripTime = int.Parse(strings[3])
}
group rawData by rawData.ServerName into server
select new
{
ServerName = server.Key,
data = new StatisticsData
{
SuccessTimes = server.Count(data => IPStatus.Success.ToString().Equals(data.IPStatus)),
TimedOutTimes = server.Count(data => IPStatus.TimedOut.ToString().Equals(data.IPStatus)),
AverageResponse = Convert.ToInt32(server.Average(data => data.RoundtripTime)),
MinResponse = server.Min(data => data.RoundtripTime),
MaxResponse = server.Max(data => data.RoundtripTime)
}
}).ToDictionary(server => server.ServerName, server => server.data);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}

//return the score by data
//server with highest score will be choosen
private static double GetScore(StatisticsData data)
{
return (double)data.SuccessTimes / (data.SuccessTimes + data.TimedOutTimes); //simply choose min package loss
}

private class StatisticsData
{
public int SuccessTimes;
public int TimedOutTimes;
public int AverageResponse;
public int MinResponse;
public int MaxResponse;
}

private void ChooseNewServer(List<Server> servers)
{
if (statistics == null)
{
return;
}
try
{
var bestResult = (from server in servers
let name = server.FriendlyName()
where statistics.ContainsKey(name)
select new
{
server,
score = GetScore(statistics[name])
}
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2);

if (_controller.GetCurrentStrategy().ID == ID && _currentServer != bestResult.server) //output when enabled
{
Console.WriteLine("Switch to server: {0} by package loss:{1}", bestResult.server.FriendlyName(), 1 - bestResult.score);
}
_currentServer = bestResult.server;
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}

public string ID
{
get { return "com.shadowsocks.strategy.scbs"; }
}

public string Name
{
get { return I18N.GetString("Choose By Total Package Loss"); }
}

public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
{
var oldServer = _currentServer;
if (oldServer == null)
{
ChooseNewServer(_controller.GetCurrentConfiguration().configs);
}
if (oldServer != _currentServer)
{
}
return _currentServer; //current server cached for CachedInterval
}

public void ReloadServers()
{
ChooseNewServer(_controller.GetCurrentConfiguration().configs);
timer?.Change(0, CachedInterval);
}

public void SetFailure(Server server)
{
Logging.Debug(String.Format("failure: {0}", server.FriendlyName()));
}

public void UpdateLastRead(Server server)
{
//TODO: combine this part of data with ICMP statics
}

public void UpdateLastWrite(Server server)
{
//TODO: combine this part of data with ICMP statics
}

public void UpdateLatency(Server server, TimeSpan latency)
{
//TODO: combine this part of data with ICMP statics
}

}
}

+ 153
- 0
shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs View File

@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;

using Newtonsoft.Json;

using Shadowsocks.Model;

namespace Shadowsocks.Controller.Strategy
{
class StatisticsStrategy : IStrategy
{
private readonly ShadowsocksController _controller;
private Server _currentServer;
private readonly Timer _timer;
private Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>> _filteredStatistics;
private int ChoiceKeptMilliseconds
=> (int) TimeSpan.FromMinutes(_controller.StatisticsConfiguration.ChoiceKeptMinutes).TotalMilliseconds;

public StatisticsStrategy(ShadowsocksController controller)
{
_controller = controller;
var servers = controller.GetCurrentConfiguration().configs;
var randomIndex = new Random().Next() % servers.Count();
_currentServer = servers[randomIndex]; //choose a server randomly at first
_timer = new Timer(ReloadStatisticsAndChooseAServer);
}

private void ReloadStatisticsAndChooseAServer(object obj)
{
Logging.Debug("Reloading statistics and choose a new server....");
var servers = _controller.GetCurrentConfiguration().configs;
LoadStatistics();
ChooseNewServer(servers);
}

private void LoadStatistics()
{
_filteredStatistics = _controller.availabilityStatistics.RawStatistics ?? _filteredStatistics ?? new Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>>();
}

//return the score by data
//server with highest score will be choosen
private float GetScore(string serverName)
{
var config = _controller.StatisticsConfiguration;
List<AvailabilityStatistics.RawStatisticsData> dataList;
if (_filteredStatistics == null || !_filteredStatistics.TryGetValue(serverName, out dataList)) return 0;
var successTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.Success.ToString()));
var timedOutTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.TimedOut.ToString()));
var statisticsData = new AvailabilityStatistics.StatisticsData
{
PackageLoss = timedOutTimes/(successTimes + timedOutTimes)*100,
AverageResponse = Convert.ToInt32(dataList.Average(data => data.RoundtripTime)),
MinResponse = dataList.Min(data => data.RoundtripTime),
MaxResponse = dataList.Max(data => data.RoundtripTime)
};
float factor;
float score = 0;
if (!config.Calculations.TryGetValue("PackageLoss", out factor)) factor = 0;
score += statisticsData.PackageLoss*factor;
if (!config.Calculations.TryGetValue("AverageResponse", out factor)) factor = 0;
score += statisticsData.AverageResponse*factor;
if (!config.Calculations.TryGetValue("MinResponse", out factor)) factor = 0;
score += statisticsData.MinResponse*factor;
if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0;
score += statisticsData.MaxResponse*factor;
Logging.Debug($"{serverName} {JsonConvert.SerializeObject(statisticsData)}");
return score;
}

private void ChooseNewServer(List<Server> servers)
{
if (_filteredStatistics == null || servers.Count == 0)
{
return;
}
try
{
var bestResult = (from server in servers
let name = server.FriendlyName()
where _filteredStatistics.ContainsKey(name)
select new
{
server,
score = GetScore(name)
}
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2);

LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by statistics: score {bestResult.score}");
_currentServer = bestResult.server;
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}

private void LogWhenEnabled(string log)
{
if (_controller.GetCurrentStrategy()?.ID == ID) //output when enabled
{
Console.WriteLine(log);
}
}

public string ID => "com.shadowsocks.strategy.scbs";

public string Name => I18N.GetString("Choose By Total Package Loss");

public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
{
var oldServer = _currentServer;
if (oldServer == null)
{
ChooseNewServer(_controller.GetCurrentConfiguration().configs);
}
if (oldServer != _currentServer)
{
}
return _currentServer; //current server cached for CachedInterval
}

public void ReloadServers()
{
ChooseNewServer(_controller.GetCurrentConfiguration().configs);
_timer?.Change(0, ChoiceKeptMilliseconds);
}

public void SetFailure(Server server)
{
Logging.Debug($"failure: {server.FriendlyName()}");
}

public void UpdateLastRead(Server server)
{
//TODO: combine this part of data with ICMP statics
}

public void UpdateLastWrite(Server server)
{
//TODO: combine this part of data with ICMP statics
}

public void UpdateLatency(Server server, TimeSpan latency)
{
//TODO: combine this part of data with ICMP statics
}
}
}

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

@@ -13,7 +13,7 @@ namespace Shadowsocks.Controller.Strategy
_strategies = new List<IStrategy>();
_strategies.Add(new BalancingStrategy(controller));
_strategies.Add(new HighAvailabilityStrategy(controller));
_strategies.Add(new SimplyChooseByStatisticsStrategy(controller));
_strategies.Add(new StatisticsStrategy(controller));
// TODO: load DLL plugins
}
public IList<IStrategy> GetStrategies()


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

@@ -6,21 +6,23 @@ namespace Shadowsocks.Controller
{
class AutoStartup
{
static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode();
public static bool Set(bool enabled)
{
RegistryKey runKey = null;
try
{
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)
{
runKey.SetValue("Shadowsocks", path);
runKey.SetValue(Key, path);
}
else
{
runKey.DeleteValue("Shadowsocks");
runKey.DeleteValue(Key);
}
runKey.Close();
return true;
}
catch (Exception e)
@@ -28,20 +30,39 @@ namespace Shadowsocks.Controller
Logging.LogUsefulException(e);
return false;
}
finally
{
if (runKey != null)
{
try { runKey.Close(); }
catch (Exception e)
{ Logging.LogUsefulException(e); }
}
}
}
public static bool Check()
{
RegistryKey runKey = null;
try
{
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();
runKey.Close();
foreach (string item in runList)
{
if (item.Equals("Shadowsocks"))
if (item.Equals(Key))
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;
}
@@ -50,6 +71,15 @@ namespace Shadowsocks.Controller
Logging.LogUsefulException(e);
return false;
}
finally
{
if (runKey != null)
{
try { runKey.Close(); }
catch(Exception e)
{ Logging.LogUsefulException(e); }
}
}
}
}
}

+ 13
- 5
shadowsocks-csharp/Data/cn.txt View File

@@ -10,6 +10,7 @@ PAC=PAC 模式
Global=全局模式
Servers=服务器
Edit Servers...=编辑服务器...
Statistics Config...=统计配置...
Start on Boot=开机启动
Allow Clients from LAN=允许来自局域网的连接
Local PAC=使用本地 PAC
@@ -21,6 +22,9 @@ Show QRCode...=显示二维码...
Scan QRCode from Screen...=扫描屏幕上的二维码...
Availability Statistics=统计可用性
Show Logs...=显示日志...
Updates...=更新...
Check for Updates...=检查更新
Check for Updates at Startup=启动时检查更新
About...=关于...
Quit=退出
Edit Servers=编辑服务器
@@ -39,6 +43,7 @@ Password=密码
Encryption=加密
Proxy Port=代理端口
Remarks=备注
Onetime Authentication (Experimental)=一次性认证(实验性)
OK=确定
Cancel=取消
New server=未配置的服务器
@@ -50,10 +55,12 @@ Move D&own=下移(&O)
&File=文件(&F)
&Open Location=在资源管理器中打开(&O)
E&xit=退出(&X)
&Clean logs=清空(&C)
&Font=字体(&F)
&Wrap text=自动换行(&W)
&Top most=置顶(&T)
&View=视图(&V)
&Clean Logs=清空日志(&C)
Change &Font=设置字体(&F)
&Wrap Text=自动换行(&W)
&Top Most=置顶(&T)
&Show Toolbar=显示工具栏(&S)
Log Viewer=日志查看器

# QRCode Form
@@ -77,7 +84,8 @@ Password can not be blank=密码不能为空
Port out of range=端口超出范围
Port can't be 8123=端口不能为 8123
Shadowsocks {0} Update Found=Shadowsocks {0} 更新
Click here to download=点击这里下载
No update is available=没有可用的更新
Click here to update=点击这里升级
Shadowsocks is here=Shadowsocks 在这里
You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks
System Proxy Enabled=系统代理已启用


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
activity-animation 0
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
hide-console
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
hide-console

+ 6
- 2
shadowsocks-csharp/Encryption/EncryptorBase.cs View File

@@ -8,19 +8,23 @@ namespace Shadowsocks.Encryption
{
public const int MAX_INPUT_SIZE = 32768;
protected EncryptorBase(string method, string password)
protected EncryptorBase(string method, string password, bool onetimeauth, bool isudp)
{
Method = method;
Password = password;
OnetimeAuth = onetimeauth;
IsUDP = isudp;
}
protected string Method;
protected string Password;
protected bool OnetimeAuth;
protected bool IsUDP;
protected byte[] GetPasswordHash()
{
byte[] inputBytes = Encoding.UTF8.GetBytes(Password);
byte[] hash = MD5.Create().ComputeHash(inputBytes);
byte[] hash = MbedTLS.MD5(inputBytes);
return hash;
}


+ 4
- 8
shadowsocks-csharp/Encryption/EncryptorFactory.cs View File

@@ -8,15 +8,11 @@ namespace Shadowsocks.Encryption
{
private static Dictionary<string, Type> _registeredEncryptors;
private static Type[] _constructorTypes = new Type[] { typeof(string), typeof(string) };
private static Type[] _constructorTypes = new Type[] { typeof(string), typeof(string), typeof(bool), typeof(bool) };
static EncryptorFactory()
{
_registeredEncryptors = new Dictionary<string, Type>();
foreach (string method in TableEncryptor.SupportedCiphers())
{
_registeredEncryptors.Add(method, typeof(TableEncryptor));
}
foreach (string method in PolarSSLEncryptor.SupportedCiphers())
{
_registeredEncryptors.Add(method, typeof(PolarSSLEncryptor));
@@ -27,16 +23,16 @@ namespace Shadowsocks.Encryption
}
}
public static IEncryptor GetEncryptor(string method, string password)
public static IEncryptor GetEncryptor(string method, string password, bool onetimeauth, bool isudp)
{
if (string.IsNullOrEmpty(method))
{
method = "table";
method = "aes-256-cfb";
}
method = method.ToLowerInvariant();
Type t = _registeredEncryptors[method];
ConstructorInfo c = t.GetConstructor(_constructorTypes);
IEncryptor result = (IEncryptor)c.Invoke(new object[] { method, password });
IEncryptor result = (IEncryptor)c.Invoke(new object[] { method, password, onetimeauth, isudp });
return result;
}
}


+ 145
- 17
shadowsocks-csharp/Encryption/IVEncryptor.cs View File

@@ -2,12 +2,24 @@
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Net;
namespace Shadowsocks.Encryption
{
public abstract class IVEncryptor
: EncryptorBase
{
public const int MAX_KEY_LENGTH = 64;
public const int MAX_IV_LENGTH = 16;
public const int ONETIMEAUTH_FLAG = 0x10;
public const int ADDRTYPE_MASK = 0xF;
public const int ONETIMEAUTH_BYTES = 10;
public const int CLEN_BYTES = 2;
public const int AUTH_BYTES = ONETIMEAUTH_BYTES + CLEN_BYTES;
protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE];
protected Dictionary<string, int[]> ciphers;
@@ -25,9 +37,11 @@ namespace Shadowsocks.Encryption
protected byte[] _key;
protected int keyLen;
protected int ivLen;
protected uint counter = 0;
protected byte[] _keyBuffer = null;
public IVEncryptor(string method, string password)
: base(method, password)
public IVEncryptor(string method, string password, bool onetimeauth, bool isudp)
: base(method, password, onetimeauth, isudp)
{
InitKey(method, password);
}
@@ -48,18 +62,22 @@ namespace Shadowsocks.Encryption
}
keyLen = ciphers[_method][0];
ivLen = ciphers[_method][1];
if (CachedKeys.ContainsKey(k))
{
_key = CachedKeys[k];
}
else
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;
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)
@@ -69,16 +87,15 @@ namespace Shadowsocks.Encryption
byte[] md5sum = null;
while (i < key.Length)
{
MD5 md5 = MD5.Create();
if (i == 0)
{
md5sum = md5.ComputeHash(password);
md5sum = MbedTLS.MD5(password);
}
else
{
md5sum.CopyTo(result, 0);
password.CopyTo(result, md5sum.Length);
md5sum = md5.ComputeHash(result);
md5sum = MbedTLS.MD5(result);
}
md5sum.CopyTo(key, i);
i += md5sum.Length;
@@ -88,7 +105,8 @@ namespace Shadowsocks.Encryption
protected static void randBytes(byte[] buf, int length)
{
byte[] temp = new byte[length];
new Random().NextBytes(temp);
RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider();
rngServiceProvider.GetBytes(temp);
temp.CopyTo(buf, 0);
}
@@ -111,14 +129,122 @@ namespace Shadowsocks.Encryption
protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf);
protected int getHeadLen(byte[] buf, int length)
{
int len = 0;
int atyp = length > 0 ? (buf[0] & ADDRTYPE_MASK) : 0;
if (atyp == 1)
{
len = 7; // atyp (1 bytes) + ipv4 (4 bytes) + port (2 bytes)
}
else if (atyp == 3 && length > 1)
{
int nameLen = buf[1];
len = 4 + nameLen; // atyp (1 bytes) + name length (1 bytes) + name (n bytes) + port (2 bytes)
}
else if (atyp == 4)
{
len = 19; // atyp (1 bytes) + ipv6 (16 bytes) + port (2 bytes)
}
if (len == 0 || len > length)
throw new Exception($"invalid header with addr type {atyp}");
return len;
}
protected byte[] genOnetimeAuthHash(byte[] msg, int msg_len)
{
byte[] auth = new byte[ONETIMEAUTH_BYTES];
byte[] hash = new byte[20];
byte[] auth_key = new byte[MAX_IV_LENGTH + MAX_KEY_LENGTH];
Buffer.BlockCopy(_encryptIV, 0, auth_key, 0, ivLen);
Buffer.BlockCopy(_key, 0, auth_key, ivLen, keyLen);
Sodium.ss_sha1_hmac_ex(auth_key, (uint)(ivLen + keyLen),
msg, 0, (uint)msg_len, hash);
Buffer.BlockCopy(hash, 0, auth, 0, ONETIMEAUTH_BYTES);
return auth;
}
protected void updateKeyBuffer()
{
if (_keyBuffer == null)
{
_keyBuffer = new byte[MAX_IV_LENGTH + 4];
Buffer.BlockCopy(_encryptIV, 0, _keyBuffer, 0, ivLen);
}
byte[] counter_bytes = BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder((int)counter));
Buffer.BlockCopy(counter_bytes, 0, _keyBuffer, ivLen, 4);
counter++;
}
protected byte[] genHash(byte[] buf, int offset, int len)
{
byte[] hash = new byte[20];
updateKeyBuffer();
Sodium.ss_sha1_hmac_ex(_keyBuffer, (uint)_keyBuffer.Length,
buf, offset, (uint)len, hash);
return hash;
}
protected void reactBuffer4TCP(byte[] buf, ref int length)
{
if (!_encryptIVSent)
{
int headLen = getHeadLen(buf, length);
int dataLen = length - headLen;
buf[0] |= ONETIMEAUTH_FLAG;
byte[] hash = genOnetimeAuthHash(buf, headLen);
Buffer.BlockCopy(buf, headLen, buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen);
Buffer.BlockCopy(hash, 0, buf, headLen, ONETIMEAUTH_BYTES);
hash = genHash(buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen);
Buffer.BlockCopy(hash, 0, buf, headLen + ONETIMEAUTH_BYTES + CLEN_BYTES, ONETIMEAUTH_BYTES);
byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)dataLen));
Buffer.BlockCopy(lenBytes, 0, buf, headLen + ONETIMEAUTH_BYTES, CLEN_BYTES);
length = headLen + ONETIMEAUTH_BYTES + AUTH_BYTES + dataLen;
}
else
{
byte[] hash = genHash(buf, 0, length);
Buffer.BlockCopy(buf, 0, buf, AUTH_BYTES, length);
byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)length));
Buffer.BlockCopy(lenBytes, 0, buf, 0, CLEN_BYTES);
Buffer.BlockCopy(hash, 0, buf, CLEN_BYTES, ONETIMEAUTH_BYTES);
length += AUTH_BYTES;
}
}
protected void reactBuffer4UDP(byte[] buf, ref int length)
{
buf[0] |= ONETIMEAUTH_FLAG;
byte[] hash = genOnetimeAuthHash(buf, length);
Buffer.BlockCopy(hash, 0, buf, length, ONETIMEAUTH_BYTES);
length += ONETIMEAUTH_BYTES;
}
protected void reactBuffer(byte[] buf, ref int length)
{
if (OnetimeAuth && ivLen > 0)
{
if (!IsUDP)
{
reactBuffer4TCP(buf, ref length);
}
else
{
reactBuffer4UDP(buf, ref length);
}
}
}
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
if (!_encryptIVSent)
{
_encryptIVSent = true;
randBytes(outbuf, ivLen);
initCipher(outbuf, true);
outlength = length + ivLen;
reactBuffer(buf, ref length);
_encryptIVSent = true;
lock (tempbuf)
{
cipherUpdate(true, length, buf, tempbuf);
@@ -128,6 +254,7 @@ namespace Shadowsocks.Encryption
}
else
{
reactBuffer(buf, ref length);
outlength = length;
cipherUpdate(true, length, buf, outbuf);
}
@@ -153,5 +280,6 @@ namespace Shadowsocks.Encryption
cipherUpdate(false, length, buf, outbuf);
}
}
}
}

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

+ 3
- 4
shadowsocks-csharp/Encryption/PolarSSLEncryptor.cs View File

@@ -16,8 +16,8 @@ namespace Shadowsocks.Encryption
private IntPtr _encryptCtx = IntPtr.Zero;
private IntPtr _decryptCtx = IntPtr.Zero;
public PolarSSLEncryptor(string method, string password)
: base(method, password)
public PolarSSLEncryptor(string method, string password, bool onetimeauth, bool isudp)
: base(method, password, onetimeauth, isudp)
{
InitKey(method, password);
}
@@ -26,7 +26,6 @@ namespace Shadowsocks.Encryption
{"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-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}},
};
@@ -61,7 +60,7 @@ namespace Shadowsocks.Encryption
realkey = new byte[keyLen];
Array.Copy(_key, 0, temp, 0, keyLen);
Array.Copy(iv, 0, temp, keyLen, ivLen);
realkey = MD5.Create().ComputeHash(temp);
realkey = MbedTLS.MD5(temp);
}
else
{


+ 18
- 12
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.Runtime.InteropServices;
using System.Text;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Encryption
{
@@ -15,19 +14,17 @@ namespace Shadowsocks.Encryption
static Sodium()
{
string tempPath = Utils.GetTempPath();
string dllPath = tempPath + "/libsscrypto.dll";
string dllPath = Utils.GetTempPath("libsscrypto.dll");
try
{
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
LoadLibrary(dllPath);
}
catch (IOException)
{
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Logging.LogUsefulException(e);
}
LoadLibrary(dllPath);
}
@@ -36,9 +33,18 @@ namespace Shadowsocks.Encryption
private static extern IntPtr LoadLibrary(string path);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k);
public extern static int crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k);
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static void 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)]
public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen,
byte[] input, int ioff, uint ilen,
byte[] output);
}
}

+ 7
- 2
shadowsocks-csharp/Encryption/SodiumEncryptor.cs View File

@@ -10,6 +10,7 @@ namespace Shadowsocks.Encryption
{
const int CIPHER_SALSA20 = 1;
const int CIPHER_CHACHA20 = 2;
const int CIPHER_CHACHA20_IETF = 3;
const int SODIUM_BLOCK_SIZE = 64;
@@ -20,8 +21,8 @@ namespace Shadowsocks.Encryption
protected ulong _encryptIC;
protected ulong _decryptIC;
public SodiumEncryptor(string method, string password)
: base(method, password)
public SodiumEncryptor(string method, string password, bool onetimeauth, bool isudp)
: base(method, password, onetimeauth, isudp)
{
InitKey(method, password);
}
@@ -29,6 +30,7 @@ namespace Shadowsocks.Encryption
private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> {
{"salsa20", new int[]{32, 8, CIPHER_SALSA20, 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()
@@ -75,6 +77,9 @@ namespace Shadowsocks.Encryption
case CIPHER_CHACHA20:
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
case CIPHER_CHACHA20_IETF:
Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key);
break;
}
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
padding += length;


+ 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)
: base(method, password)
{
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()
{
}
}
}

+ 5
- 0
shadowsocks-csharp/FodyWeavers.xml View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers>
<Costura/>
</Weavers>

+ 14
- 56
shadowsocks-csharp/Model/Configuration.cs View File

@@ -1,9 +1,9 @@
using Shadowsocks.Controller;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Shadowsocks.Controller;
using Newtonsoft.Json;
namespace Shadowsocks.Model
{
@@ -23,19 +23,17 @@ namespace Shadowsocks.Model
public string pacUrl;
public bool useOnlinePac;
public bool availabilityStatistics;
public bool autoCheckUpdate;
public LogViewerConfig logViewer;
private static string CONFIG_FILE = "gui-config.json";
public Server GetCurrentServer()
{
if (index >= 0 && index < configs.Count)
{
return configs[index];
}
else
{
return GetDefaultServer();
}
}
public static void CheckServer(Server server)
@@ -50,32 +48,24 @@ namespace Shadowsocks.Model
try
{
string configContent = File.ReadAllText(CONFIG_FILE);
Configuration config = SimpleJson.SimpleJson.DeserializeObject<Configuration>(configContent, new JsonSerializerStrategy());
Configuration config = JsonConvert.DeserializeObject<Configuration>(configContent);
config.isDefault = false;
if (config.localPort == 0)
{
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;
}
catch (Exception e)
{
if (!(e is FileNotFoundException))
{
Console.WriteLine(e);
}
Logging.LogUsefulException(e);
return new Configuration
{
index = 0,
isDefault = true,
localPort = 1080,
autoCheckUpdate = true,
configs = new List<Server>()
{
GetDefaultServer()
@@ -87,26 +77,17 @@ namespace Shadowsocks.Model
public static void Save(Configuration config)
{
if (config.index >= config.configs.Count)
{
config.index = config.configs.Count - 1;
}
if (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;
try
{
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.Flush();
}
@@ -125,55 +106,32 @@ namespace Shadowsocks.Model
private static void Assert(bool condition)
{
if (!condition)
{
throw new Exception(I18N.GetString("assertion failure"));
}
}
public static void CheckPort(int port)
{
if (port <= 0 || port > 65535)
{
throw new ArgumentException(I18N.GetString("Port out of range"));
}
}
public static void CheckLocalPort(int port)
{
CheckPort(port);
if (port == 8123)
{
throw new ArgumentException(I18N.GetString("Port can't be 8123"));
}
}
private static void CheckPassword(string password)
{
if (string.IsNullOrEmpty(password))
{
throw new ArgumentException(I18N.GetString("Password can not be blank"));
}
}
private static void CheckServer(string server)
{
if (string.IsNullOrEmpty(server))
{
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);
}
}
}
}

+ 103
- 0
shadowsocks-csharp/Model/LogViewerConfig.cs View File

@@ -0,0 +1,103 @@
using Shadowsocks.View;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Shadowsocks.Model
{
[Serializable]
public class LogViewerConfig
{
public string fontName;
public float fontSize;
public string bgColor;
public string textColor;
public bool topMost;
public bool wrapText;
public bool toolbarShown;
public int width;
public int height;
public int top;
public int left;
public LogViewerConfig()
{
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()
{
try
{
return new Font(fontName, fontSize, FontStyle.Regular);
}
catch (Exception)
{
return new Font("Console", 8F);
}
}
public void SetFont(Font font)
{
fontName = font.Name;
fontSize = font.Size;
}
public Color GetBackgroundColor()
{
try
{
return ColorTranslator.FromHtml(bgColor);
}
catch (Exception)
{
return ColorTranslator.FromHtml("black");
}
}
public void SetBackgroundColor(Color color)
{
bgColor = ColorTranslator.ToHtml(color);
}
public Color GetTextColor()
{
try
{
return ColorTranslator.FromHtml(textColor);
}
catch (Exception)
{
return ColorTranslator.FromHtml("white");
}
}
public void SetTextColor(Color color)
{
textColor = ColorTranslator.ToHtml(color);
}
}
}

+ 17
- 16
shadowsocks-csharp/Model/Server.cs View File

@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using SimpleJson;
using Shadowsocks.Controller;
using System.Text.RegularExpressions;
using Shadowsocks.Controller;
namespace Shadowsocks.Model
{
[Serializable]
@@ -17,6 +14,7 @@ namespace Shadowsocks.Model
public string password;
public string method;
public string remarks;
public bool auth;
public override int GetHashCode()
{
@@ -26,7 +24,7 @@ namespace Shadowsocks.Model
public override bool Equals(object 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()
@@ -47,11 +45,12 @@ namespace Shadowsocks.Model
public Server()
{
this.server = "";
this.server_port = 8388;
this.method = "aes-256-cfb";
this.password = "";
this.remarks = "";
server = "";
server_port = 8388;
method = "aes-256-cfb";
password = "";
remarks = "";
auth = false;
}
public Server(string ssURL) : this()
@@ -63,7 +62,7 @@ namespace Shadowsocks.Model
{
try
{
bytes = System.Convert.FromBase64String(base64);
bytes = Convert.FromBase64String(base64);
}
catch (FormatException)
{
@@ -81,13 +80,15 @@ namespace Shadowsocks.Model
string afterAt = data.Substring(indexLastAt + 1);
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[] parts = beforeAt.Split(new[] { ':' });
this.method = parts[0];
this.password = parts[1];
method = parts[0];
password = parts[1];
//TODO: read one_time_auth
}
catch (IndexOutOfRangeException)
{


+ 106
- 0
shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

using Newtonsoft.Json;

using Shadowsocks.Controller;

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

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

public static StatisticsStrategyConfiguration Load()
{
try
{
var content = File.ReadAllText(ConfigFile);
var configuration = JsonConvert.DeserializeObject<StatisticsStrategyConfiguration>(content);
return configuration;
}
catch (FileNotFoundException)
{
var configuration = new StatisticsStrategyConfiguration();
Save(configuration);
return configuration;
}
catch (Exception e)
{
Logging.LogUsefulException(e);
return new StatisticsStrategyConfiguration();
}
}

public static void Save(StatisticsStrategyConfiguration configuration)
{
try
{
var content = JsonConvert.SerializeObject(configuration, Formatting.Indented);
File.WriteAllText(ConfigFile, content);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}

public Dictionary<string, float> Calculations;

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

public bool StatisticsEnabled
{
get { return _statisticsEnabled; }
set { _statisticsEnabled = value; }
}

public bool ByIsp
{
get { return _byIsp; }
set { _byIsp = value; }
}

public bool ByHourOfDay
{
get { return _byHourOfDay; }
set { _byHourOfDay = value; }
}

public int ChoiceKeptMinutes
{
get { return _choiceKeptMinutes; }
set { _choiceKeptMinutes = value; }
}

public int DataCollectionMinutes
{
get { return _dataCollectionMinutes; }
set { _dataCollectionMinutes = value; }
}

public int RepeatTimesNum
{
get { return _repeatTimesNum; }
set { _repeatTimesNum = value; }
}
}
}

+ 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.IO;
using System.Threading;
using System.Windows.Forms;
using Shadowsocks.Controller;
using Shadowsocks.Util;
using Shadowsocks.View;
namespace Shadowsocks
{
static class Program
@@ -18,7 +18,7 @@ namespace Shadowsocks
[STAThread]
static void Main()
{
Util.Utils.ReleaseMemory(true);
Utils.ReleaseMemory(true);
using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode()))
{
Application.EnableVisualStyles();
@@ -37,15 +37,19 @@ namespace Shadowsocks
return;
}
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();
#endif
ShadowsocksController controller = new ShadowsocksController();
MenuViewController viewController = new MenuViewController(controller);
controller.Start();
Application.Run();
}
}


+ 10
- 0
shadowsocks-csharp/Properties/DataSources/Shadowsocks.Model.StatisticsStrategyConfiguration.datasource View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is automatically generated by Visual Studio .Net. It is
used to store generic object data source configuration information.
Renaming the file extension or editing the content of this file may
cause the file to be unrecognizable by the program.
-->
<GenericObjectDataSource DisplayName="StatisticsStrategyConfiguration" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
<TypeInfo>Shadowsocks.Model.StatisticsStrategyConfiguration, Shadowsocks, Version=2.5.2.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
</GenericObjectDataSource>

+ 38
- 40
shadowsocks-csharp/Properties/Resources.Designer.cs View File

@@ -10,8 +10,8 @@
namespace Shadowsocks.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
@@ -31,7 +31,7 @@ namespace Shadowsocks.Properties {
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
@@ -45,7 +45,7 @@ namespace Shadowsocks.Properties {
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
@@ -59,7 +59,7 @@ namespace Shadowsocks.Properties {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@@ -69,39 +69,37 @@ namespace Shadowsocks.Properties {
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to # translation for Simplified Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=启用系统代理
///Mode=系统代理模式
///PAC=PAC 模式
///Global=全局模式
///Servers=服务器
///Edit Servers...=编辑服务器...
///Start on Boot=开机启动
///Allow Clients from LAN=允许来自局域网的连接
///Local PAC=使用本地 PAC
///Online PAC=使用在线 PAC
///Edit Local PAC File...=编辑本地 PAC 文件...
///Update Local PAC from GFWList=从 GFWList 更新本地 PAC
///Edit User Rule for GFWList...=编辑 GFWList 的用户规则...
///Show QRCode...=显示二维码...
///Scan QRCode from Screen...=扫描屏幕上的二维码...
///Show Logs...=显示日志...
///About...=关于...
///Quit=退出 [rest of string was truncated]&quot;;.
/// Looks up a localized string similar to # translation for Simplified Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=启用系统代理
///Mode=系统代理模式
///PAC=PAC 模式
///Global=全局模式
///Servers=服务器
///Edit Servers...=编辑服务器...
///Start on Boot=开机启动
///Allow Clients from LAN=允许来自局域网的连接
///Local PAC=使用本地 PAC
///Online PAC=使用在线 PAC
///Edit Local PAC File...=编辑本地 PAC 文件...
///Update Local PAC from GFWList=从 GFWList 更新本地 PAC
///Edit User Rule for GFWList...=编辑 GFWList 的用户规则...
///Show QRCode...=显示二维码...
///Scan QRCode from Screen...=扫描屏幕上的二维码...
///Availability Statistic [rest of string was truncated]&quot;;.
/// </summary>
internal static string cn {
get {
return ResourceManager.GetString("cn", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@@ -111,7 +109,7 @@ namespace Shadowsocks.Properties {
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@@ -121,7 +119,7 @@ namespace Shadowsocks.Properties {
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to listen-address __POLIPO_BIND_IP__:8123
///show-on-task-bar 0
@@ -134,7 +132,7 @@ namespace Shadowsocks.Properties {
return ResourceManager.GetString("privoxy_conf", resourceCulture);
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@@ -144,7 +142,7 @@ namespace Shadowsocks.Properties {
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
@@ -154,7 +152,7 @@ namespace Shadowsocks.Properties {
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -164,7 +162,7 @@ namespace Shadowsocks.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -174,7 +172,7 @@ namespace Shadowsocks.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -184,7 +182,7 @@ namespace Shadowsocks.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
@@ -194,7 +192,7 @@ namespace Shadowsocks.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized string similar to ! Put user rules line by line in this file.
///! See https://adblockplus.org/en/filter-cheatsheet


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

@@ -1,32 +1,48 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Shadowsocks.Controller;
namespace Shadowsocks.Util
{
public class Utils
{
private static string TempPath = null;
// return path to store temporary files
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)
@@ -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")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetProcessWorkingSetSize(IntPtr process,


+ 111
- 0
shadowsocks-csharp/View/CalculationControl.Designer.cs View File

@@ -0,0 +1,111 @@
namespace Shadowsocks.View
{
partial class CalculationControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Component Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.factorNum = new System.Windows.Forms.NumericUpDown();
this.multiply = new System.Windows.Forms.Label();
this.plus = new System.Windows.Forms.Label();
this.valueLabel = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.factorNum)).BeginInit();
this.SuspendLayout();
//
// factorNum
//
this.factorNum.DecimalPlaces = 2;
this.factorNum.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.factorNum.Increment = new decimal(new int[] {
1,
0,
0,
131072});
this.factorNum.Location = new System.Drawing.Point(285, 5);
this.factorNum.Minimum = new decimal(new int[] {
1000,
0,
0,
-2147418112});
this.factorNum.Name = "factorNum";
this.factorNum.Size = new System.Drawing.Size(86, 34);
this.factorNum.TabIndex = 6;
//
// multiply
//
this.multiply.AutoSize = true;
this.multiply.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.multiply.Location = new System.Drawing.Point(251, 7);
this.multiply.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.multiply.Name = "multiply";
this.multiply.Size = new System.Drawing.Size(26, 28);
this.multiply.TabIndex = 2;
this.multiply.Text = "×";
//
// plus
//
this.plus.AutoSize = true;
this.plus.Font = new System.Drawing.Font("Segoe UI", 10F);
this.plus.Location = new System.Drawing.Point(5, 7);
this.plus.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.plus.Name = "plus";
this.plus.Size = new System.Drawing.Size(26, 28);
this.plus.TabIndex = 3;
this.plus.Text = "+";
//
// valueLabel
//
this.valueLabel.AutoSize = true;
this.valueLabel.Font = new System.Drawing.Font("Microsoft YaHei", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.valueLabel.Location = new System.Drawing.Point(39, 11);
this.valueLabel.Name = "valueLabel";
this.valueLabel.Size = new System.Drawing.Size(118, 24);
this.valueLabel.TabIndex = 7;
this.valueLabel.Text = "PackageLoss";
//
// CalculationControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.valueLabel);
this.Controls.Add(this.factorNum);
this.Controls.Add(this.multiply);
this.Controls.Add(this.plus);
this.Name = "CalculationControl";
this.Size = new System.Drawing.Size(380, 46);
((System.ComponentModel.ISupportInitialize)(this.factorNum)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion
private System.Windows.Forms.NumericUpDown factorNum;
private System.Windows.Forms.Label multiply;
private System.Windows.Forms.Label plus;
private System.Windows.Forms.Label valueLabel;
}
}

+ 25
- 0
shadowsocks-csharp/View/CalculationControl.cs View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Shadowsocks.View
{
public partial class CalculationControl : UserControl
{
public CalculationControl(string text, float value)
{
InitializeComponent();
valueLabel.Text = text;
factorNum.Value = (decimal) value;
}

public string Value => valueLabel.Text;
public float Factor => (float) factorNum.Value;
}
}

+ 120
- 0
shadowsocks-csharp/View/CalculationControl.resx View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

+ 72
- 58
shadowsocks-csharp/View/ConfigForm.Designer.cs View File

@@ -3,14 +3,14 @@
partial class ConfigForm
{
/// <summary>
/// 必需的设计器变量。
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
@@ -20,11 +20,11 @@
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
#region Windows Form Designer generated code
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
@@ -39,6 +39,7 @@
this.PasswordTextBox = new System.Windows.Forms.TextBox();
this.EncryptionLabel = new System.Windows.Forms.Label();
this.EncryptionSelect = new System.Windows.Forms.ComboBox();
this.OneTimeAuth = new System.Windows.Forms.CheckBox();
this.panel2 = new System.Windows.Forms.Panel();
this.OKButton = new System.Windows.Forms.Button();
this.MyCancelButton = new System.Windows.Forms.Button();
@@ -81,37 +82,39 @@
this.tableLayoutPanel1.Controls.Add(this.PasswordTextBox, 1, 2);
this.tableLayoutPanel1.Controls.Add(this.EncryptionLabel, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.EncryptionSelect, 1, 3);
this.tableLayoutPanel1.Controls.Add(this.OneTimeAuth, 1, 6);
this.tableLayoutPanel1.Location = new System.Drawing.Point(8, 21);
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(3);
this.tableLayoutPanel1.RowCount = 6;
this.tableLayoutPanel1.RowCount = 7;
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.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(238, 137);
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(249, 162);
this.tableLayoutPanel1.TabIndex = 0;
//
// RemarksTextBox
//
this.RemarksTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.RemarksTextBox.Location = new System.Drawing.Point(72, 111);
this.RemarksTextBox.Location = new System.Drawing.Point(83, 113);
this.RemarksTextBox.MaxLength = 32;
this.RemarksTextBox.Name = "RemarksTextBox";
this.RemarksTextBox.Size = new System.Drawing.Size(160, 20);
this.RemarksTextBox.TabIndex = 10;
this.RemarksTextBox.Size = new System.Drawing.Size(160, 21);
this.RemarksTextBox.TabIndex = 4;
this.RemarksTextBox.WordWrap = false;
//
// RemarksLabel
//
this.RemarksLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.RemarksLabel.AutoSize = true;
this.RemarksLabel.Location = new System.Drawing.Point(17, 114);
this.RemarksLabel.Location = new System.Drawing.Point(30, 117);
this.RemarksLabel.Name = "RemarksLabel";
this.RemarksLabel.Size = new System.Drawing.Size(49, 13);
this.RemarksLabel.Size = new System.Drawing.Size(47, 12);
this.RemarksLabel.TabIndex = 9;
this.RemarksLabel.Text = "Remarks";
//
@@ -119,9 +122,9 @@
//
this.IPLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.IPLabel.AutoSize = true;
this.IPLabel.Location = new System.Drawing.Point(15, 9);
this.IPLabel.Location = new System.Drawing.Point(18, 10);
this.IPLabel.Name = "IPLabel";
this.IPLabel.Size = new System.Drawing.Size(51, 13);
this.IPLabel.Size = new System.Drawing.Size(59, 12);
this.IPLabel.TabIndex = 0;
this.IPLabel.Text = "Server IP";
//
@@ -129,9 +132,9 @@
//
this.ServerPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.ServerPortLabel.AutoSize = true;
this.ServerPortLabel.Location = new System.Drawing.Point(6, 35);
this.ServerPortLabel.Location = new System.Drawing.Point(6, 37);
this.ServerPortLabel.Name = "ServerPortLabel";
this.ServerPortLabel.Size = new System.Drawing.Size(60, 13);
this.ServerPortLabel.Size = new System.Drawing.Size(71, 12);
this.ServerPortLabel.TabIndex = 1;
this.ServerPortLabel.Text = "Server Port";
//
@@ -139,40 +142,40 @@
//
this.PasswordLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.PasswordLabel.AutoSize = true;
this.PasswordLabel.Location = new System.Drawing.Point(13, 61);
this.PasswordLabel.Location = new System.Drawing.Point(24, 64);
this.PasswordLabel.Name = "PasswordLabel";
this.PasswordLabel.Size = new System.Drawing.Size(53, 13);
this.PasswordLabel.Size = new System.Drawing.Size(53, 12);
this.PasswordLabel.TabIndex = 2;
this.PasswordLabel.Text = "Password";
//
// IPTextBox
//
this.IPTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.IPTextBox.Location = new System.Drawing.Point(72, 6);
this.IPTextBox.Location = new System.Drawing.Point(83, 6);
this.IPTextBox.MaxLength = 512;
this.IPTextBox.Name = "IPTextBox";
this.IPTextBox.Size = new System.Drawing.Size(160, 20);
this.IPTextBox.Size = new System.Drawing.Size(160, 21);
this.IPTextBox.TabIndex = 0;
this.IPTextBox.WordWrap = false;
//
// ServerPortTextBox
//
this.ServerPortTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.ServerPortTextBox.Location = new System.Drawing.Point(72, 32);
this.ServerPortTextBox.Location = new System.Drawing.Point(83, 33);
this.ServerPortTextBox.MaxLength = 10;
this.ServerPortTextBox.Name = "ServerPortTextBox";
this.ServerPortTextBox.Size = new System.Drawing.Size(160, 20);
this.ServerPortTextBox.Size = new System.Drawing.Size(160, 21);
this.ServerPortTextBox.TabIndex = 1;
this.ServerPortTextBox.WordWrap = false;
//
// PasswordTextBox
//
this.PasswordTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.PasswordTextBox.Location = new System.Drawing.Point(72, 58);
this.PasswordTextBox.Location = new System.Drawing.Point(83, 60);
this.PasswordTextBox.MaxLength = 256;
this.PasswordTextBox.Name = "PasswordTextBox";
this.PasswordTextBox.PasswordChar = '*';
this.PasswordTextBox.Size = new System.Drawing.Size(160, 20);
this.PasswordTextBox.Size = new System.Drawing.Size(160, 21);
this.PasswordTextBox.TabIndex = 2;
this.PasswordTextBox.WordWrap = false;
//
@@ -180,33 +183,43 @@
//
this.EncryptionLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.EncryptionLabel.AutoSize = true;
this.EncryptionLabel.Location = new System.Drawing.Point(9, 88);
this.EncryptionLabel.Location = new System.Drawing.Point(12, 91);
this.EncryptionLabel.Name = "EncryptionLabel";
this.EncryptionLabel.Size = new System.Drawing.Size(57, 13);
this.EncryptionLabel.Size = new System.Drawing.Size(65, 12);
this.EncryptionLabel.TabIndex = 8;
this.EncryptionLabel.Text = "Encryption";
//
// EncryptionSelect
//
this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.EncryptionSelect.FormattingEnabled = true;
this.EncryptionSelect.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.EncryptionSelect.ItemHeight = 13;
this.EncryptionSelect.ItemHeight = 12;
this.EncryptionSelect.Items.AddRange(new object[] {
"table",
"rc4-md5",
"salsa20",
"chacha20",
"chacha20-ietf",
"aes-256-cfb",
"aes-192-cfb",
"aes-128-cfb",
"rc4"});
this.EncryptionSelect.Location = new System.Drawing.Point(72, 84);
"aes-128-cfb"});
this.EncryptionSelect.Location = new System.Drawing.Point(83, 87);
this.EncryptionSelect.Name = "EncryptionSelect";
this.EncryptionSelect.Size = new System.Drawing.Size(160, 21);
this.EncryptionSelect.Size = new System.Drawing.Size(160, 20);
this.EncryptionSelect.TabIndex = 3;
this.EncryptionSelect.SelectedIndexChanged += new System.EventHandler(this.EncryptionSelect_SelectedIndexChanged);
//
// OneTimeAuth
//
this.OneTimeAuth.AutoSize = true;
this.OneTimeAuth.Location = new System.Drawing.Point(83, 140);
this.OneTimeAuth.Name = "OneTimeAuth";
this.OneTimeAuth.Size = new System.Drawing.Size(156, 16);
this.OneTimeAuth.TabIndex = 5;
this.OneTimeAuth.Text = "Onetime Authentication";
this.OneTimeAuth.UseVisualStyleBackColor = true;
//
// panel2
//
@@ -226,7 +239,7 @@
this.OKButton.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0);
this.OKButton.Name = "OKButton";
this.OKButton.Size = new System.Drawing.Size(75, 23);
this.OKButton.TabIndex = 8;
this.OKButton.TabIndex = 12;
this.OKButton.Text = "OK";
this.OKButton.UseVisualStyleBackColor = true;
this.OKButton.Click += new System.EventHandler(this.OKButton_Click);
@@ -239,7 +252,7 @@
this.MyCancelButton.Margin = new System.Windows.Forms.Padding(3, 3, 0, 0);
this.MyCancelButton.Name = "MyCancelButton";
this.MyCancelButton.Size = new System.Drawing.Size(75, 23);
this.MyCancelButton.TabIndex = 9;
this.MyCancelButton.TabIndex = 13;
this.MyCancelButton.Text = "Cancel";
this.MyCancelButton.UseVisualStyleBackColor = true;
this.MyCancelButton.Click += new System.EventHandler(this.CancelButton_Click);
@@ -251,7 +264,7 @@
this.DeleteButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3);
this.DeleteButton.Name = "DeleteButton";
this.DeleteButton.Size = new System.Drawing.Size(80, 23);
this.DeleteButton.TabIndex = 7;
this.DeleteButton.TabIndex = 9;
this.DeleteButton.Text = "&Delete";
this.DeleteButton.UseVisualStyleBackColor = true;
this.DeleteButton.Click += new System.EventHandler(this.DeleteButton_Click);
@@ -263,7 +276,7 @@
this.AddButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3);
this.AddButton.Name = "AddButton";
this.AddButton.Size = new System.Drawing.Size(80, 23);
this.AddButton.TabIndex = 6;
this.AddButton.TabIndex = 8;
this.AddButton.Text = "&Add";
this.AddButton.UseVisualStyleBackColor = true;
this.AddButton.Click += new System.EventHandler(this.AddButton_Click);
@@ -276,8 +289,8 @@
this.ServerGroupBox.Location = new System.Drawing.Point(178, 0);
this.ServerGroupBox.Margin = new System.Windows.Forms.Padding(12, 0, 0, 0);
this.ServerGroupBox.Name = "ServerGroupBox";
this.ServerGroupBox.Size = new System.Drawing.Size(249, 174);
this.ServerGroupBox.TabIndex = 6;
this.ServerGroupBox.Size = new System.Drawing.Size(260, 200);
this.ServerGroupBox.TabIndex = 0;
this.ServerGroupBox.TabStop = false;
this.ServerGroupBox.Text = "Server";
//
@@ -290,7 +303,7 @@
this.ServersListBox.Margin = new System.Windows.Forms.Padding(0);
this.ServersListBox.Name = "ServersListBox";
this.ServersListBox.Size = new System.Drawing.Size(166, 148);
this.ServersListBox.TabIndex = 5;
this.ServersListBox.TabIndex = 7;
this.ServersListBox.SelectedIndexChanged += new System.EventHandler(this.ServersListBox_SelectedIndexChanged);
//
// tableLayoutPanel2
@@ -313,7 +326,7 @@
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238);
this.tableLayoutPanel2.Size = new System.Drawing.Size(438, 265);
this.tableLayoutPanel2.TabIndex = 7;
//
// tableLayoutPanel6
@@ -326,7 +339,7 @@
this.tableLayoutPanel6.Controls.Add(this.MoveDownButton, 1, 0);
this.tableLayoutPanel6.Controls.Add(this.MoveUpButton, 0, 0);
this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Top;
this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 211);
this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 233);
this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel6.Name = "tableLayoutPanel6";
this.tableLayoutPanel6.RowCount = 1;
@@ -341,7 +354,7 @@
this.MoveDownButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3);
this.MoveDownButton.Name = "MoveDownButton";
this.MoveDownButton.Size = new System.Drawing.Size(80, 23);
this.MoveDownButton.TabIndex = 7;
this.MoveDownButton.TabIndex = 11;
this.MoveDownButton.Text = "Move D&own";
this.MoveDownButton.UseVisualStyleBackColor = true;
this.MoveDownButton.Click += new System.EventHandler(this.MoveDownButton_Click);
@@ -353,7 +366,7 @@
this.MoveUpButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3);
this.MoveUpButton.Name = "MoveUpButton";
this.MoveUpButton.Size = new System.Drawing.Size(80, 23);
this.MoveUpButton.TabIndex = 6;
this.MoveUpButton.TabIndex = 10;
this.MoveUpButton.Text = "Move &Up";
this.MoveUpButton.UseVisualStyleBackColor = true;
this.MoveUpButton.Click += new System.EventHandler(this.MoveUpButton_Click);
@@ -369,36 +382,36 @@
this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel5.Controls.Add(this.ProxyPortTextBox, 1, 0);
this.tableLayoutPanel5.Controls.Add(this.ProxyPortLabel, 0, 0);
this.tableLayoutPanel5.Location = new System.Drawing.Point(241, 174);
this.tableLayoutPanel5.Location = new System.Drawing.Point(242, 200);
this.tableLayoutPanel5.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel5.Name = "tableLayoutPanel5";
this.tableLayoutPanel5.Padding = new System.Windows.Forms.Padding(3);
this.tableLayoutPanel5.RowCount = 1;
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F));
this.tableLayoutPanel5.Size = new System.Drawing.Size(186, 32);
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F));
this.tableLayoutPanel5.Size = new System.Drawing.Size(196, 33);
this.tableLayoutPanel5.TabIndex = 9;
//
// ProxyPortTextBox
//
this.ProxyPortTextBox.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.ProxyPortTextBox.Location = new System.Drawing.Point(67, 6);
this.ProxyPortTextBox.Location = new System.Drawing.Point(77, 6);
this.ProxyPortTextBox.MaxLength = 10;
this.ProxyPortTextBox.Name = "ProxyPortTextBox";
this.ProxyPortTextBox.Size = new System.Drawing.Size(113, 20);
this.ProxyPortTextBox.TabIndex = 4;
this.ProxyPortTextBox.Size = new System.Drawing.Size(113, 21);
this.ProxyPortTextBox.TabIndex = 6;
this.ProxyPortTextBox.WordWrap = false;
//
// ProxyPortLabel
//
this.ProxyPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.ProxyPortLabel.AutoSize = true;
this.ProxyPortLabel.Location = new System.Drawing.Point(6, 9);
this.ProxyPortLabel.Location = new System.Drawing.Point(6, 10);
this.ProxyPortLabel.Name = "ProxyPortLabel";
this.ProxyPortLabel.Size = new System.Drawing.Size(55, 13);
this.ProxyPortLabel.Size = new System.Drawing.Size(65, 12);
this.ProxyPortLabel.TabIndex = 3;
this.ProxyPortLabel.Text = "Proxy Port";
//
@@ -413,7 +426,7 @@
this.tableLayoutPanel3.Controls.Add(this.MyCancelButton, 1, 0);
this.tableLayoutPanel3.Controls.Add(this.OKButton, 0, 0);
this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Right;
this.tableLayoutPanel3.Location = new System.Drawing.Point(268, 209);
this.tableLayoutPanel3.Location = new System.Drawing.Point(279, 236);
this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3);
this.tableLayoutPanel3.Name = "tableLayoutPanel3";
this.tableLayoutPanel3.RowCount = 1;
@@ -431,7 +444,7 @@
this.tableLayoutPanel4.Controls.Add(this.DeleteButton, 1, 0);
this.tableLayoutPanel4.Controls.Add(this.AddButton, 0, 0);
this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Top;
this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 174);
this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 200);
this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel4.Name = "tableLayoutPanel4";
this.tableLayoutPanel4.RowCount = 1;
@@ -505,6 +518,7 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6;
private System.Windows.Forms.Button MoveDownButton;
private System.Windows.Forms.Button MoveUpButton;
private System.Windows.Forms.CheckBox OneTimeAuth;
}
}

+ 44
- 3
shadowsocks-csharp/View/ConfigForm.cs View File

@@ -48,6 +48,7 @@ namespace Shadowsocks.View
EncryptionLabel.Text = I18N.GetString("Encryption");
ProxyPortLabel.Text = I18N.GetString("Proxy Port");
RemarksLabel.Text = I18N.GetString("Remarks");
OneTimeAuth.Text = I18N.GetString("Onetime Authentication (Experimental)");
ServerGroupBox.Text = I18N.GetString("Server");
OKButton.Text = I18N.GetString("OK");
MyCancelButton.Text = I18N.GetString("Cancel");
@@ -78,11 +79,12 @@ namespace Shadowsocks.View
}
Server server = new Server
{
server = IPTextBox.Text,
server = IPTextBox.Text.Trim(),
server_port = int.Parse(ServerPortTextBox.Text),
password = PasswordTextBox.Text,
method = EncryptionSelect.Text,
remarks = RemarksTextBox.Text
remarks = RemarksTextBox.Text,
auth = OneTimeAuth.Checked
};
int localPort = int.Parse(ProxyPortTextBox.Text);
Configuration.CheckServer(server);
@@ -115,6 +117,7 @@ namespace Shadowsocks.View
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString();
EncryptionSelect.Text = server.method ?? "aes-256-cfb";
RemarksTextBox.Text = server.remarks;
OneTimeAuth.Checked = server.auth;
}
}
@@ -146,6 +149,28 @@ namespace Shadowsocks.View
}
private void ConfigForm_KeyDown(object sender, KeyEventArgs e)
{
// Sometimes the users may hit enter key by mistake, and the form will close without saving entries.
if (e.KeyCode == Keys.Enter)
{
Server server = controller.GetCurrentServer();
if (!SaveOldSelectedServer())
{
return;
}
if (_modifiedConfiguration.configs.Count == 0)
{
MessageBox.Show(I18N.GetString("Please add at least one server"));
return;
}
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort);
controller.SelectServerIndex(_modifiedConfiguration.configs.IndexOf(server));
}
}
private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (!ServersListBox.CanSelect)
@@ -163,7 +188,10 @@ namespace Shadowsocks.View
ServersListBox.SelectedIndex = _lastSelectedIndex;
return;
}
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName();
if (_lastSelectedIndex >= 0)
{
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName();
}
UpdateMoveUpAndDownButton();
LoadSelectedServer();
_lastSelectedIndex = ServersListBox.SelectedIndex;
@@ -297,5 +325,18 @@ namespace Shadowsocks.View
MoveConfigItem(+1); // +1 means move forward
}
}
private void EncryptionSelect_SelectedIndexChanged(object sender, EventArgs e)
{
if (EncryptionSelect.Text == "rc4" || EncryptionSelect.Text == "table")
{
OneTimeAuth.Enabled = false;
OneTimeAuth.Checked = false;
}
else
{
OneTimeAuth.Enabled = true;
}
}
}
}

+ 116
- 54
shadowsocks-csharp/View/LogForm.Designer.cs View File

@@ -30,19 +30,25 @@
{
this.components = new System.ComponentModel.Container();
this.LogMessageTextBox = new System.Windows.Forms.TextBox();
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components);
this.MainMenu = new System.Windows.Forms.MainMenu(this.components);
this.FileMenuItem = new System.Windows.Forms.MenuItem();
this.OpenLocationMenuItem = new System.Windows.Forms.MenuItem();
this.ExitMenuItem = new System.Windows.Forms.MenuItem();
this.panel1 = new System.Windows.Forms.Panel();
this.ViewMenuItem = new System.Windows.Forms.MenuItem();
this.CleanLogsMenuItem = new System.Windows.Forms.MenuItem();
this.ChangeFontMenuItem = new System.Windows.Forms.MenuItem();
this.WrapTextMenuItem = new System.Windows.Forms.MenuItem();
this.TopMostMenuItem = new System.Windows.Forms.MenuItem();
this.MenuItemSeparater = new System.Windows.Forms.MenuItem();
this.ShowToolbarMenuItem = new System.Windows.Forms.MenuItem();
this.TopMostCheckBox = new System.Windows.Forms.CheckBox();
this.ChangeFontButton = new System.Windows.Forms.Button();
this.CleanLogsButton = new System.Windows.Forms.Button();
this.WrapTextCheckBox = new System.Windows.Forms.CheckBox();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.TopMostCheckBox = new System.Windows.Forms.CheckBox();
this.panel1.SuspendLayout();
this.ToolbarFlowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel();
this.tableLayoutPanel1.SuspendLayout();
this.ToolbarFlowLayoutPanel.SuspendLayout();
this.SuspendLayout();
//
// LogMessageTextBox
@@ -51,25 +57,20 @@
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.ForeColor = System.Drawing.Color.White;
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 43);
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 40);
this.LogMessageTextBox.MaxLength = 2147483647;
this.LogMessageTextBox.Multiline = true;
this.LogMessageTextBox.Name = "LogMessageTextBox";
this.LogMessageTextBox.ReadOnly = true;
this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.LogMessageTextBox.Size = new System.Drawing.Size(541, 307);
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 131);
this.LogMessageTextBox.TabIndex = 0;
this.LogMessageTextBox.WordWrap = false;
//
// contextMenuStrip1
//
this.contextMenuStrip1.Name = "contextMenuStrip1";
this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4);
//
// mainMenu1
// MainMenu
//
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.FileMenuItem});
this.MainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.FileMenuItem,
this.ViewMenuItem});
//
// FileMenuItem
//
@@ -91,23 +92,72 @@
this.ExitMenuItem.Text = "E&xit";
this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click);
//
// panel1
// ViewMenuItem
//
this.ViewMenuItem.Index = 1;
this.ViewMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.CleanLogsMenuItem,
this.ChangeFontMenuItem,
this.WrapTextMenuItem,
this.TopMostMenuItem,
this.MenuItemSeparater,
this.ShowToolbarMenuItem});
this.ViewMenuItem.Text = "&View";
//
// CleanLogsMenuItem
//
this.CleanLogsMenuItem.Index = 0;
this.CleanLogsMenuItem.Text = "&Clean Logs";
this.CleanLogsMenuItem.Click += new System.EventHandler(this.CleanLogsMenuItem_Click);
//
// ChangeFontMenuItem
//
this.ChangeFontMenuItem.Index = 1;
this.ChangeFontMenuItem.Text = "Change &Font";
this.ChangeFontMenuItem.Click += new System.EventHandler(this.ChangeFontMenuItem_Click);
//
// WrapTextMenuItem
//
this.WrapTextMenuItem.Index = 2;
this.WrapTextMenuItem.Text = "&Wrap Text";
this.WrapTextMenuItem.Click += new System.EventHandler(this.WrapTextMenuItem_Click);
//
// TopMostMenuItem
//
this.TopMostMenuItem.Index = 3;
this.TopMostMenuItem.Text = "&Top Most";
this.TopMostMenuItem.Click += new System.EventHandler(this.TopMostMenuItem_Click);
//
// MenuItemSeparater
//
this.MenuItemSeparater.Index = 4;
this.MenuItemSeparater.Text = "-";
//
// ShowToolbarMenuItem
//
this.panel1.Controls.Add(this.TopMostCheckBox);
this.panel1.Controls.Add(this.ChangeFontButton);
this.panel1.Controls.Add(this.CleanLogsButton);
this.panel1.Controls.Add(this.WrapTextCheckBox);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(3, 3);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(541, 34);
this.panel1.TabIndex = 1;
this.ShowToolbarMenuItem.Index = 5;
this.ShowToolbarMenuItem.Text = "&Show Toolbar";
this.ShowToolbarMenuItem.Click += new System.EventHandler(this.ShowToolbarMenuItem_Click);
//
// TopMostCheckBox
//
this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.TopMostCheckBox.AutoSize = true;
this.TopMostCheckBox.Location = new System.Drawing.Point(247, 3);
this.TopMostCheckBox.Name = "TopMostCheckBox";
this.TopMostCheckBox.Size = new System.Drawing.Size(71, 25);
this.TopMostCheckBox.TabIndex = 3;
this.TopMostCheckBox.Text = "&Top Most";
this.TopMostCheckBox.UseVisualStyleBackColor = true;
this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged);
//
// ChangeFontButton
//
this.ChangeFontButton.Location = new System.Drawing.Point(107, 4);
this.ChangeFontButton.AutoSize = true;
this.ChangeFontButton.Location = new System.Drawing.Point(84, 3);
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.Text = "&Font";
this.ChangeFontButton.UseVisualStyleBackColor = true;
@@ -115,22 +165,25 @@
//
// CleanLogsButton
//
this.CleanLogsButton.Location = new System.Drawing.Point(9, 4);
this.CleanLogsButton.AutoSize = true;
this.CleanLogsButton.Location = new System.Drawing.Point(3, 3);
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.Text = "&Clean logs";
this.CleanLogsButton.Text = "&Clean Logs";
this.CleanLogsButton.UseVisualStyleBackColor = true;
this.CleanLogsButton.Click += new System.EventHandler(this.CleanLogsButton_Click);
//
// WrapTextCheckBox
//
this.WrapTextCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.WrapTextCheckBox.AutoSize = true;
this.WrapTextCheckBox.Location = new System.Drawing.Point(209, 9);
this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3);
this.WrapTextCheckBox.Name = "WrapTextCheckBox";
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 16);
this.WrapTextCheckBox.Size = new System.Drawing.Size(76, 25);
this.WrapTextCheckBox.TabIndex = 0;
this.WrapTextCheckBox.Text = "&Wrap text";
this.WrapTextCheckBox.Text = "&Wrap Text";
this.WrapTextCheckBox.UseVisualStyleBackColor = true;
this.WrapTextCheckBox.CheckedChanged += new System.EventHandler(this.WrapTextCheckBox_CheckedChanged);
//
@@ -138,45 +191,48 @@
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.LogMessageTextBox, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.ToolbarFlowLayoutPanel, 0, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(547, 353);
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 174);
this.tableLayoutPanel1.TabIndex = 2;
//
// TopMostCheckBox
// ToolbarFlowLayoutPanel
//
this.TopMostCheckBox.AutoSize = true;
this.TopMostCheckBox.Location = new System.Drawing.Point(311, 9);
this.TopMostCheckBox.Name = "TopMostCheckBox";
this.TopMostCheckBox.Size = new System.Drawing.Size(72, 16);
this.TopMostCheckBox.TabIndex = 3;
this.TopMostCheckBox.Text = "&Top most";
this.TopMostCheckBox.UseVisualStyleBackColor = true;
this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged);
this.ToolbarFlowLayoutPanel.AutoSize = true;
this.ToolbarFlowLayoutPanel.Controls.Add(this.CleanLogsButton);
this.ToolbarFlowLayoutPanel.Controls.Add(this.ChangeFontButton);
this.ToolbarFlowLayoutPanel.Controls.Add(this.WrapTextCheckBox);
this.ToolbarFlowLayoutPanel.Controls.Add(this.TopMostCheckBox);
this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3);
this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel";
this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 31);
this.ToolbarFlowLayoutPanel.TabIndex = 2;
//
// 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.ClientSize = new System.Drawing.Size(547, 353);
this.ClientSize = new System.Drawing.Size(384, 174);
this.Controls.Add(this.tableLayoutPanel1);
this.Menu = this.mainMenu1;
this.Menu = this.MainMenu;
this.MinimumSize = new System.Drawing.Size(400, 213);
this.Name = "LogForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Log Viewer";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing);
this.Load += new System.EventHandler(this.LogForm_Load);
this.Shown += new System.EventHandler(this.LogForm_Shown);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ToolbarFlowLayoutPanel.ResumeLayout(false);
this.ToolbarFlowLayoutPanel.PerformLayout();
this.ResumeLayout(false);
}
@@ -184,16 +240,22 @@
#endregion
private System.Windows.Forms.TextBox LogMessageTextBox;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.MainMenu MainMenu;
private System.Windows.Forms.MenuItem FileMenuItem;
private System.Windows.Forms.MenuItem OpenLocationMenuItem;
private System.Windows.Forms.MenuItem ExitMenuItem;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.CheckBox WrapTextCheckBox;
private System.Windows.Forms.Button CleanLogsButton;
private System.Windows.Forms.Button ChangeFontButton;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.CheckBox TopMostCheckBox;
private System.Windows.Forms.MenuItem ViewMenuItem;
private System.Windows.Forms.MenuItem CleanLogsMenuItem;
private System.Windows.Forms.MenuItem ChangeFontMenuItem;
private System.Windows.Forms.MenuItem WrapTextMenuItem;
private System.Windows.Forms.MenuItem TopMostMenuItem;
private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel;
private System.Windows.Forms.MenuItem MenuItemSeparater;
private System.Windows.Forms.MenuItem ShowToolbarMenuItem;
}
}

+ 179
- 28
shadowsocks-csharp/View/LogForm.cs View File

@@ -1,15 +1,13 @@
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Model;
using Shadowsocks.Util;
namespace Shadowsocks.View
{
public partial class LogForm : Form
@@ -18,12 +16,28 @@ namespace Shadowsocks.View
string filename;
Timer timer;
const int BACK_OFFSET = 65536;
ShadowsocksController controller;
public LogForm(string filename)
public LogForm(ShadowsocksController controller, string filename)
{
this.controller = controller;
this.filename = filename;
InitializeComponent();
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
if (config == null)
{
config = new LogViewerConfig();
}
else {
topMostTrigger = config.topMost;
wrapTextTrigger = config.wrapText;
toolbarTrigger = config.toolbarShown;
LogMessageTextBox.BackColor = config.GetBackgroundColor();
LogMessageTextBox.ForeColor = config.GetTextColor();
LogMessageTextBox.Font = config.GetFont();
}
UpdateTexts();
}
@@ -33,11 +47,17 @@ namespace Shadowsocks.View
FileMenuItem.Text = I18N.GetString("&File");
OpenLocationMenuItem.Text = I18N.GetString("&Open Location");
ExitMenuItem.Text = I18N.GetString("E&xit");
CleanLogsButton.Text = I18N.GetString("&Clean logs");
ChangeFontButton.Text = I18N.GetString("&Font");
WrapTextCheckBox.Text = I18N.GetString("&Wrap text");
TopMostCheckBox.Text = I18N.GetString("&Top most");
this.Text = I18N.GetString("Log Viewer");
CleanLogsButton.Text = I18N.GetString("&Clean Logs");
ChangeFontButton.Text = I18N.GetString("Change &Font");
WrapTextCheckBox.Text = I18N.GetString("&Wrap Text");
TopMostCheckBox.Text = I18N.GetString("&Top Most");
ViewMenuItem.Text = I18N.GetString("&View");
CleanLogsMenuItem.Text = I18N.GetString("&Clean Logs");
ChangeFontMenuItem.Text = I18N.GetString("Change &Font");
WrapTextMenuItem.Text = I18N.GetString("&Wrap Text");
TopMostMenuItem.Text = I18N.GetString("&Top Most");
ShowToolbarMenuItem.Text = I18N.GetString("&Show Toolbar");
Text = I18N.GetString("Log Viewer");
}
private void Timer_Tick(object sender, EventArgs e)
@@ -58,7 +78,7 @@ namespace Shadowsocks.View
string line = "";
while ((line = reader.ReadLine()) != null)
LogMessageTextBox.AppendText(line + "\r\n");
LogMessageTextBox.AppendText(line + Environment.NewLine);
LogMessageTextBox.ScrollToCaret();
@@ -78,7 +98,7 @@ namespace Shadowsocks.View
while ((line = reader.ReadLine()) != null)
{
changed = true;
LogMessageTextBox.AppendText(line + "\r\n");
LogMessageTextBox.AppendText(line + Environment.NewLine);
}
if (changed)
@@ -88,32 +108,68 @@ namespace Shadowsocks.View
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)
{
InitContent();
timer = new Timer();
timer.Interval = 300;
timer.Tick += Timer_Tick;
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;
TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
topMostTriggerLock = false;
wrapTextTriggerLock = true;
LogMessageTextBox.WordWrap = WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger;
wrapTextTriggerLock = false;
ToolbarFlowLayoutPanel.Visible = ShowToolbarMenuItem.Checked = toolbarTrigger;
}
private void LogForm_FormClosing(object sender, FormClosingEventArgs e)
{
timer.Stop();
LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
if (config == null)
config = new LogViewerConfig();
config.topMost = topMostTrigger;
config.wrapText = wrapTextTrigger;
config.toolbarShown = toolbarTrigger;
config.SetFont(LogMessageTextBox.Font);
config.SetBackgroundColor(LogMessageTextBox.BackColor);
config.SetTextColor(LogMessageTextBox.ForeColor);
config.top = Top;
config.left = Left;
config.height = Height;
config.width = Width;
controller.SaveLogViewerConfig(config);
}
private void OpenLocationMenuItem_Click(object sender, EventArgs e)
{
string argument = "/select, \"" + filename + "\"";
Console.WriteLine(argument);
Logging.Debug(argument);
System.Diagnostics.Process.Start("explorer.exe", argument);
}
private void ExitMenuItem_Click(object sender, EventArgs e)
{
this.Close();
Close();
}
private void LogForm_Shown(object sender, EventArgs e)
@@ -121,30 +177,125 @@ namespace Shadowsocks.View
LogMessageTextBox.ScrollToCaret();
}
private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e)
#region Clean up the content in LogMessageTextBox.
private void DoCleanLogs()
{
LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked;
LogMessageTextBox.ScrollToCaret();
LogMessageTextBox.Clear();
}
private void CleanLogsMenuItem_Click(object sender, EventArgs e)
{
DoCleanLogs();
}
private void CleanLogsButton_Click(object sender, EventArgs e)
{
LogMessageTextBox.Clear();
DoCleanLogs();
}
#endregion
#region Change the font settings applied in LogMessageTextBox.
private void DoChangeFont()
{
try
{
FontDialog fd = new FontDialog();
fd.Font = LogMessageTextBox.Font;
if (fd.ShowDialog() == DialogResult.OK)
{
LogMessageTextBox.Font = new Font(fd.Font.FontFamily, fd.Font.Size, fd.Font.Style);
}
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
MessageBox.Show(ex.Message);
}
}
private void ChangeFontMenuItem_Click(object sender, EventArgs e)
{
DoChangeFont();
}
private void ChangeFontButton_Click(object sender, EventArgs e)
{
FontDialog fd = new FontDialog();
fd.Font = LogMessageTextBox.Font;
if (fd.ShowDialog() == DialogResult.OK)
DoChangeFont();
}
#endregion
#region Trigger the log messages to wrapable, or not.
bool wrapTextTrigger = false;
bool wrapTextTriggerLock = false;
private void TriggerWrapText()
{
wrapTextTriggerLock = true;
wrapTextTrigger = !wrapTextTrigger;
LogMessageTextBox.WordWrap = wrapTextTrigger;
LogMessageTextBox.ScrollToCaret();
WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger;
wrapTextTriggerLock = false;
}
private void WrapTextMenuItem_Click(object sender, EventArgs e)
{
if (!wrapTextTriggerLock)
{
TriggerWrapText();
}
}
private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (!wrapTextTriggerLock)
{
LogMessageTextBox.Font = fd.Font;
TriggerWrapText();
}
}
#endregion
#region Trigger the window to top most, or not.
bool topMostTrigger = false;
bool topMostTriggerLock = false;
private void TriggerTopMost()
{
topMostTriggerLock = true;
topMostTrigger = !topMostTrigger;
TopMost = topMostTrigger;
TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
topMostTriggerLock = false;
}
private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e)
{
this.TopMost = TopMostCheckBox.Checked;
if (!topMostTriggerLock)
{
TriggerTopMost();
}
}
private void TopMostMenuItem_Click(object sender, EventArgs e)
{
if (!topMostTriggerLock)
{
TriggerTopMost();
}
}
#endregion
private bool toolbarTrigger = false;
private void ShowToolbarMenuItem_Click(object sender, EventArgs e)
{
toolbarTrigger = !toolbarTrigger;
ToolbarFlowLayoutPanel.Visible = toolbarTrigger;
ShowToolbarMenuItem.Checked = toolbarTrigger;
}
}
}

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

@@ -117,10 +117,31 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="contextMenuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<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">
<value>17, 17</value>
</metadata>
<metadata name="mainMenu1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>172, 17</value>
<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>

+ 130
- 29
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.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.View
{
public class MenuViewController
@@ -26,10 +28,10 @@ namespace Shadowsocks.View
private ContextMenu contextMenu1;
private bool _isFirstRun;
private bool _isStartupChecking;
private MenuItem enableItem;
private MenuItem modeItem;
private MenuItem AutoStartupItem;
private MenuItem AvailabilityStatistics;
private MenuItem ShareOverLANItem;
private MenuItem SeperatorItem;
private MenuItem ConfigItem;
@@ -42,7 +44,10 @@ namespace Shadowsocks.View
private MenuItem updateFromGFWListItem;
private MenuItem editGFWUserRuleItem;
private MenuItem editOnlinePACItem;
private MenuItem autoCheckUpdatesToggleItem;
private ConfigForm configForm;
private List<LogForm> logForms = new List<LogForm>();
private bool logFormsVisible = false;
private string _urlToOpen;
public MenuViewController(ShadowsocksController controller)
@@ -65,16 +70,25 @@ namespace Shadowsocks.View
UpdateTrayIcon();
_notifyIcon.Visible = true;
_notifyIcon.ContextMenu = contextMenu1;
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
_notifyIcon.MouseClick += notifyIcon1_Click;
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
this.updateChecker = new UpdateChecker();
updateChecker.NewVersionFound += updateChecker_NewVersionFound;
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted;
LoadCurrentConfiguration();
updateChecker.CheckUpdate(controller.GetConfigurationCopy());
Configuration config = controller.GetConfigurationCopy();
if (controller.GetConfigurationCopy().isDefault)
if (config.autoCheckUpdate)
{
_isStartupChecking = true;
updateChecker.CheckUpdate(config, 3000);
}
if (config.isDefault)
{
_isFirstRun = true;
ShowConfigForm();
@@ -164,6 +178,7 @@ namespace Shadowsocks.View
this.ServersItem = CreateMenuGroup("Servers", new MenuItem[] {
this.SeperatorItem = new MenuItem("-"),
this.ConfigItem = CreateMenuItem("Edit Servers...", new EventHandler(this.Config_Click)),
CreateMenuItem("Statistics Config...", StatisticsConfigItem_Click),
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)),
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click))
}),
@@ -178,10 +193,14 @@ namespace Shadowsocks.View
}),
new MenuItem("-"),
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.AvailabilityStatistics = CreateMenuItem("Availability Statistics", new EventHandler(this.AvailabilityStatisticsItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
CreateMenuGroup("Updates...", new MenuItem[] {
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new MenuItem("-"),
this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
}),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
new MenuItem("-"),
CreateMenuItem("Quit", new EventHandler(this.Quit_Click))
@@ -238,19 +257,41 @@ namespace Shadowsocks.View
ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000);
}
void updateChecker_NewVersionFound(object sender, EventArgs e)
void updateChecker_CheckUpdateCompleted(object sender, EventArgs e)
{
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to download"), ToolTipIcon.Info, 5000);
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
_isFirstRun = false;
if (updateChecker.NewVersionFound)
{
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000);
_isFirstRun = false;
}
else if (!_isStartupChecking)
{
ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000);
_isFirstRun = false;
}
_isStartupChecking = false;
}
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
{
System.Diagnostics.Process.Start(updateChecker.LatestVersionURL);
_notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked;
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()
{
@@ -262,10 +303,10 @@ namespace Shadowsocks.View
PACModeItem.Checked = !config.global;
ShareOverLANItem.Checked = config.shareOverLan;
AutoStartupItem.Checked = AutoStartup.Check();
AvailabilityStatistics.Checked = config.availabilityStatistics;
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked;
UpdatePACItemsEnabledStatus();
UpdateUpdateMenu();
}
private void UpdateServersMenu()
@@ -301,7 +342,6 @@ namespace Shadowsocks.View
{
item.Checked = true;
}
}
}
@@ -319,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)
{
configForm = null;
Util.Utils.ReleaseMemory(true);
Utils.ReleaseMemory(true);
ShowFirstTimeBalloon();
}
@@ -343,7 +409,7 @@ namespace Shadowsocks.View
if (_isFirstRun)
{
_notifyIcon.BalloonTipTitle = I18N.GetString("Shadowsocks is here");
_notifyIcon.BalloonTipText = I18N.GetString("You can turn on/off Shadowsocks in the context menu");
_notifyIcon.BalloonTipText = I18N.GetString("You can turn on/off Shadowsocks in the context menu");
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info;
_notifyIcon.ShowBalloonTip(0);
_isFirstRun = false;
@@ -355,6 +421,18 @@ namespace Shadowsocks.View
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)
{
if (e.Button == MouseButtons.Left)
@@ -413,9 +491,17 @@ namespace Shadowsocks.View
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(argument).Show();
logForms.Add(f);
}
private void StatisticsConfigItem_Click(object sender, EventArgs e)
{
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller);
form.Show();
}
private void QRCodeItem_Click(object sender, EventArgs e)
@@ -520,18 +606,15 @@ namespace Shadowsocks.View
Process.Start(_urlToOpen);
}
private void AutoStartupItem_Click(object sender, EventArgs e) {
private void AutoStartupItem_Click(object sender, EventArgs e)
{
AutoStartupItem.Checked = !AutoStartupItem.Checked;
if (!AutoStartup.Set(AutoStartupItem.Checked)) {
if (!AutoStartup.Set(AutoStartupItem.Checked))
{
MessageBox.Show(I18N.GetString("Failed to update registry"));
}
}
private void AvailabilityStatisticsItem_Click(object sender, EventArgs e) {
AvailabilityStatistics.Checked = !AvailabilityStatistics.Checked;
controller.ToggleAvailabilityStatistics(AvailabilityStatistics.Checked);
}
private void LocalPACItem_Click(object sender, EventArgs e)
{
if (!localPACItem.Checked)
@@ -591,5 +674,23 @@ namespace Shadowsocks.View
this.editOnlinePACItem.Enabled = true;
}
}
private void UpdateUpdateMenu()
{
Configuration configuration = controller.GetConfigurationCopy();
autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate;
}
private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e)
{
Configuration configuration = controller.GetConfigurationCopy();
controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate);
UpdateUpdateMenu();
}
private void checkUpdatesItem_Click(object sender, EventArgs e)
{
updateChecker.CheckUpdate(controller.GetConfigurationCopy());
}
}
}

+ 531
- 0
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs View File

@@ -0,0 +1,531 @@
namespace Shadowsocks.View
{
partial class StatisticsStrategyConfigurationForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series();
this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart();
this.byISPCheckBox = new System.Windows.Forms.CheckBox();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.chartModeSelector = new System.Windows.Forms.GroupBox();
this.allMode = new System.Windows.Forms.RadioButton();
this.dayMode = new System.Windows.Forms.RadioButton();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.splitContainer2 = new System.Windows.Forms.SplitContainer();
this.label9 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.dataCollectionMinutesNum = new System.Windows.Forms.NumericUpDown();
this.StatisticsEnabledCheckBox = new System.Windows.Forms.CheckBox();
this.choiceKeptMinutesNum = new System.Windows.Forms.NumericUpDown();
this.byHourOfDayCheckBox = new System.Windows.Forms.CheckBox();
this.repeatTimesNum = new System.Windows.Forms.NumericUpDown();
this.label6 = new System.Windows.Forms.Label();
this.splitContainer3 = new System.Windows.Forms.SplitContainer();
this.label1 = new System.Windows.Forms.Label();
this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel();
this.serverSelector = new System.Windows.Forms.ComboBox();
this.CancelButton = new System.Windows.Forms.Button();
this.OKButton = new System.Windows.Forms.Button();
this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components);
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit();
this.chartModeSelector.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit();
this.splitContainer2.Panel1.SuspendLayout();
this.splitContainer2.Panel2.SuspendLayout();
this.splitContainer2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dataCollectionMinutesNum)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.choiceKeptMinutesNum)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.repeatTimesNum)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).BeginInit();
this.splitContainer3.Panel1.SuspendLayout();
this.splitContainer3.Panel2.SuspendLayout();
this.splitContainer3.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit();
this.SuspendLayout();
//
// StatisticsChart
//
this.StatisticsChart.BackColor = System.Drawing.Color.Transparent;
chartArea1.AxisX.MajorGrid.Enabled = false;
chartArea1.AxisY.MajorGrid.Enabled = false;
chartArea1.AxisY2.MajorGrid.Enabled = false;
chartArea1.BackColor = System.Drawing.Color.Transparent;
chartArea1.Name = "DataArea";
this.StatisticsChart.ChartAreas.Add(chartArea1);
this.StatisticsChart.Dock = System.Windows.Forms.DockStyle.Fill;
legend1.BackColor = System.Drawing.Color.Transparent;
legend1.Name = "ChartLegend";
this.StatisticsChart.Legends.Add(legend1);
this.StatisticsChart.Location = new System.Drawing.Point(0, 0);
this.StatisticsChart.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.StatisticsChart.Name = "StatisticsChart";
this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel;
series1.ChartArea = "DataArea";
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble;
series1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(221)))), ((int)(((byte)(88)))), ((int)(((byte)(0)))));
series1.Legend = "ChartLegend";
series1.Name = "Package Loss";
series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime;
series1.YValuesPerPoint = 2;
series2.BorderWidth = 4;
series2.ChartArea = "DataArea";
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(155)))), ((int)(((byte)(77)))), ((int)(((byte)(150)))));
series2.Legend = "ChartLegend";
series2.Name = "Ping";
series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime;
this.StatisticsChart.Series.Add(series1);
this.StatisticsChart.Series.Add(series2);
this.StatisticsChart.Size = new System.Drawing.Size(1077, 303);
this.StatisticsChart.TabIndex = 2;
//
// byISPCheckBox
//
this.byISPCheckBox.AutoSize = true;
this.byISPCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "ByIsp", true));
this.byISPCheckBox.Location = new System.Drawing.Point(13, 54);
this.byISPCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.byISPCheckBox.Name = "byISPCheckBox";
this.byISPCheckBox.Size = new System.Drawing.Size(220, 31);
this.byISPCheckBox.TabIndex = 5;
this.byISPCheckBox.Text = "By ISP/geolocation";
this.byISPCheckBox.UseVisualStyleBackColor = true;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(8, 136);
this.label2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(167, 27);
this.label2.TabIndex = 8;
this.label2.Text = "Keep choice for ";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(285, 136);
this.label3.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(87, 27);
this.label3.TabIndex = 9;
this.label3.Text = "minutes";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(8, 218);
this.label4.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(54, 27);
this.label4.TabIndex = 10;
this.label4.Text = "Ping";
//
// chartModeSelector
//
this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.chartModeSelector.Controls.Add(this.allMode);
this.chartModeSelector.Controls.Add(this.dayMode);
this.chartModeSelector.Location = new System.Drawing.Point(801, 104);
this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.chartModeSelector.Name = "chartModeSelector";
this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.chartModeSelector.Size = new System.Drawing.Size(261, 103);
this.chartModeSelector.TabIndex = 3;
this.chartModeSelector.TabStop = false;
this.chartModeSelector.Text = "Chart Mode";
this.chartModeSelector.Enter += new System.EventHandler(this.chartModeSelector_Enter);
//
// allMode
//
this.allMode.AutoSize = true;
this.allMode.Checked = true;
this.allMode.Location = new System.Drawing.Point(11, 61);
this.allMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.allMode.Name = "allMode";
this.allMode.Size = new System.Drawing.Size(58, 31);
this.allMode.TabIndex = 1;
this.allMode.TabStop = true;
this.allMode.Text = "all";
this.allMode.UseVisualStyleBackColor = true;
this.allMode.CheckedChanged += new System.EventHandler(this.allMode_CheckedChanged);
//
// dayMode
//
this.dayMode.AutoSize = true;
this.dayMode.Location = new System.Drawing.Point(11, 29);
this.dayMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.dayMode.Name = "dayMode";
this.dayMode.Size = new System.Drawing.Size(73, 31);
this.dayMode.TabIndex = 0;
this.dayMode.Text = "24h";
this.dayMode.UseVisualStyleBackColor = true;
this.dayMode.CheckedChanged += new System.EventHandler(this.dayMode_CheckedChanged);
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.IsSplitterFixed = true;
this.splitContainer1.Location = new System.Drawing.Point(0, 0);
this.splitContainer1.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.splitContainer1.Name = "splitContainer1";
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.splitContainer2);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.serverSelector);
this.splitContainer1.Panel2.Controls.Add(this.CancelButton);
this.splitContainer1.Panel2.Controls.Add(this.OKButton);
this.splitContainer1.Panel2.Controls.Add(this.chartModeSelector);
this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart);
this.splitContainer1.Size = new System.Drawing.Size(1077, 614);
this.splitContainer1.SplitterDistance = 301;
this.splitContainer1.SplitterWidth = 10;
this.splitContainer1.TabIndex = 12;
//
// splitContainer2
//
this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
this.splitContainer2.IsSplitterFixed = true;
this.splitContainer2.Location = new System.Drawing.Point(0, 0);
this.splitContainer2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.splitContainer2.Name = "splitContainer2";
//
// splitContainer2.Panel1
//
this.splitContainer2.Panel1.Controls.Add(this.label9);
this.splitContainer2.Panel1.Controls.Add(this.label8);
this.splitContainer2.Panel1.Controls.Add(this.dataCollectionMinutesNum);
this.splitContainer2.Panel1.Controls.Add(this.StatisticsEnabledCheckBox);
this.splitContainer2.Panel1.Controls.Add(this.choiceKeptMinutesNum);
this.splitContainer2.Panel1.Controls.Add(this.byHourOfDayCheckBox);
this.splitContainer2.Panel1.Controls.Add(this.repeatTimesNum);
this.splitContainer2.Panel1.Controls.Add(this.label6);
this.splitContainer2.Panel1.Controls.Add(this.label2);
this.splitContainer2.Panel1.Controls.Add(this.label4);
this.splitContainer2.Panel1.Controls.Add(this.byISPCheckBox);
this.splitContainer2.Panel1.Controls.Add(this.label3);
//
// splitContainer2.Panel2
//
this.splitContainer2.Panel2.Controls.Add(this.splitContainer3);
this.splitContainer2.Size = new System.Drawing.Size(1077, 301);
this.splitContainer2.SplitterDistance = 384;
this.splitContainer2.SplitterWidth = 5;
this.splitContainer2.TabIndex = 7;
//
// label9
//
this.label9.AutoSize = true;
this.label9.Location = new System.Drawing.Point(8, 175);
this.label9.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(162, 27);
this.label9.TabIndex = 20;
this.label9.Text = "Collect data per";
//
// label8
//
this.label8.AutoSize = true;
this.label8.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label8.Location = new System.Drawing.Point(285, 176);
this.label8.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(87, 27);
this.label8.TabIndex = 19;
this.label8.Text = "minutes";
//
// dataCollectionMinutesNum
//
this.dataCollectionMinutesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "DataCollectionMinutes", true));
this.dataCollectionMinutesNum.Increment = new decimal(new int[] {
10,
0,
0,
0});
this.dataCollectionMinutesNum.Location = new System.Drawing.Point(176, 173);
this.dataCollectionMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.dataCollectionMinutesNum.Maximum = new decimal(new int[] {
120,
0,
0,
0});
this.dataCollectionMinutesNum.Minimum = new decimal(new int[] {
5,
0,
0,
0});
this.dataCollectionMinutesNum.Name = "dataCollectionMinutesNum";
this.dataCollectionMinutesNum.Size = new System.Drawing.Size(100, 34);
this.dataCollectionMinutesNum.TabIndex = 18;
this.dataCollectionMinutesNum.Value = new decimal(new int[] {
10,
0,
0,
0});
//
// StatisticsEnabledCheckBox
//
this.StatisticsEnabledCheckBox.AutoSize = true;
this.StatisticsEnabledCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "StatisticsEnabled", true));
this.StatisticsEnabledCheckBox.Location = new System.Drawing.Point(13, 12);
this.StatisticsEnabledCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.StatisticsEnabledCheckBox.Name = "StatisticsEnabledCheckBox";
this.StatisticsEnabledCheckBox.Size = new System.Drawing.Size(189, 31);
this.StatisticsEnabledCheckBox.TabIndex = 17;
this.StatisticsEnabledCheckBox.Text = "Enable Statistics";
this.StatisticsEnabledCheckBox.UseVisualStyleBackColor = true;
//
// choiceKeptMinutesNum
//
this.choiceKeptMinutesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "ChoiceKeptMinutes", true));
this.choiceKeptMinutesNum.Increment = new decimal(new int[] {
10,
0,
0,
0});
this.choiceKeptMinutesNum.Location = new System.Drawing.Point(176, 134);
this.choiceKeptMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.choiceKeptMinutesNum.Maximum = new decimal(new int[] {
120,
0,
0,
0});
this.choiceKeptMinutesNum.Minimum = new decimal(new int[] {
5,
0,
0,
0});
this.choiceKeptMinutesNum.Name = "choiceKeptMinutesNum";
this.choiceKeptMinutesNum.Size = new System.Drawing.Size(100, 34);
this.choiceKeptMinutesNum.TabIndex = 16;
this.choiceKeptMinutesNum.Value = new decimal(new int[] {
10,
0,
0,
0});
//
// byHourOfDayCheckBox
//
this.byHourOfDayCheckBox.AutoSize = true;
this.byHourOfDayCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "ByHourOfDay", true));
this.byHourOfDayCheckBox.Location = new System.Drawing.Point(13, 95);
this.byHourOfDayCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.byHourOfDayCheckBox.Name = "byHourOfDayCheckBox";
this.byHourOfDayCheckBox.Size = new System.Drawing.Size(180, 31);
this.byHourOfDayCheckBox.TabIndex = 15;
this.byHourOfDayCheckBox.Text = "By hour of day";
this.byHourOfDayCheckBox.UseVisualStyleBackColor = true;
//
// repeatTimesNum
//
this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true));
this.repeatTimesNum.Location = new System.Drawing.Point(72, 216);
this.repeatTimesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.repeatTimesNum.Maximum = new decimal(new int[] {
10,
0,
0,
0});
this.repeatTimesNum.Name = "repeatTimesNum";
this.repeatTimesNum.Size = new System.Drawing.Size(99, 34);
this.repeatTimesNum.TabIndex = 14;
this.repeatTimesNum.Value = new decimal(new int[] {
4,
0,
0,
0});
//
// label6
//
this.label6.AutoSize = true;
this.label6.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label6.Location = new System.Drawing.Point(178, 218);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(201, 27);
this.label6.TabIndex = 13;
this.label6.Text = "packages everytime";
//
// splitContainer3
//
this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
this.splitContainer3.IsSplitterFixed = true;
this.splitContainer3.Location = new System.Drawing.Point(0, 0);
this.splitContainer3.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.splitContainer3.Name = "splitContainer3";
this.splitContainer3.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer3.Panel1
//
this.splitContainer3.Panel1.Controls.Add(this.label1);
//
// splitContainer3.Panel2
//
this.splitContainer3.Panel2.Controls.Add(this.calculationContainer);
this.splitContainer3.Size = new System.Drawing.Size(688, 301);
this.splitContainer3.SplitterDistance = 46;
this.splitContainer3.SplitterWidth = 10;
this.splitContainer3.TabIndex = 6;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(5, 12);
this.label1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(262, 27);
this.label1.TabIndex = 0;
this.label1.Text = "Design evaluation method";
//
// calculationContainer
//
this.calculationContainer.AutoScroll = true;
this.calculationContainer.Dock = System.Windows.Forms.DockStyle.Fill;
this.calculationContainer.Location = new System.Drawing.Point(0, 0);
this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.calculationContainer.Name = "calculationContainer";
this.calculationContainer.Size = new System.Drawing.Size(688, 245);
this.calculationContainer.TabIndex = 1;
//
// serverSelector
//
this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.serverSelector.FormattingEnabled = true;
this.serverSelector.Location = new System.Drawing.Point(801, 67);
this.serverSelector.Name = "serverSelector";
this.serverSelector.Size = new System.Drawing.Size(260, 35);
this.serverSelector.TabIndex = 6;
this.serverSelector.SelectedIndexChanged += new System.EventHandler(this.serverSelector_SelectedIndexChanged);
//
// CancelButton
//
this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.CancelButton.Location = new System.Drawing.Point(960, 220);
this.CancelButton.Name = "CancelButton";
this.CancelButton.Size = new System.Drawing.Size(101, 41);
this.CancelButton.TabIndex = 5;
this.CancelButton.Text = "Cancel";
this.CancelButton.UseVisualStyleBackColor = true;
this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click);
//
// OKButton
//
this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.OKButton.Location = new System.Drawing.Point(852, 220);
this.OKButton.Name = "OKButton";
this.OKButton.Size = new System.Drawing.Size(101, 41);
this.OKButton.TabIndex = 4;
this.OKButton.Text = "OK";
this.OKButton.UseVisualStyleBackColor = true;
this.OKButton.Click += new System.EventHandler(this.OKButton_Click);
//
// bindingConfiguration
//
this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration);
//
// StatisticsStrategyConfigurationForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.ClientSize = new System.Drawing.Size(1077, 614);
this.Controls.Add(this.splitContainer1);
this.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.MinimumSize = new System.Drawing.Size(1059, 498);
this.Name = "StatisticsStrategyConfigurationForm";
this.Text = "StatisticsStrategyConfigurationForm";
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit();
this.chartModeSelector.ResumeLayout(false);
this.chartModeSelector.PerformLayout();
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
this.splitContainer2.Panel1.ResumeLayout(false);
this.splitContainer2.Panel1.PerformLayout();
this.splitContainer2.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit();
this.splitContainer2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dataCollectionMinutesNum)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.choiceKeptMinutesNum)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.repeatTimesNum)).EndInit();
this.splitContainer3.Panel1.ResumeLayout(false);
this.splitContainer3.Panel1.PerformLayout();
this.splitContainer3.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit();
this.splitContainer3.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit();
this.ResumeLayout(false);

}

#endregion
private System.Windows.Forms.DataVisualization.Charting.Chart StatisticsChart;
private System.Windows.Forms.CheckBox byISPCheckBox;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.GroupBox chartModeSelector;
private System.Windows.Forms.RadioButton allMode;
private System.Windows.Forms.RadioButton dayMode;
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.SplitContainer splitContainer2;
private System.Windows.Forms.FlowLayoutPanel calculationContainer;
private System.Windows.Forms.SplitContainer splitContainer3;
private System.Windows.Forms.NumericUpDown repeatTimesNum;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.CheckBox byHourOfDayCheckBox;
private System.Windows.Forms.NumericUpDown choiceKeptMinutesNum;
private System.Windows.Forms.CheckBox StatisticsEnabledCheckBox;
private System.Windows.Forms.Label label9;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.NumericUpDown dataCollectionMinutesNum;
private System.Windows.Forms.BindingSource bindingConfiguration;
private new System.Windows.Forms.Button CancelButton;
private System.Windows.Forms.Button OKButton;
private System.Windows.Forms.ComboBox serverSelector;
}
}

+ 141
- 0
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.NetworkInformation;
using System.Windows.Forms;

using Shadowsocks.Controller;
using Shadowsocks.Model;

namespace Shadowsocks.View
{
public partial class StatisticsStrategyConfigurationForm : Form
{
private readonly ShadowsocksController _controller;
private StatisticsStrategyConfiguration _configuration;
private DataTable _dataTable = new DataTable();
private List<string> _servers;

public StatisticsStrategyConfigurationForm(ShadowsocksController controller)
{
if (controller == null) return;
InitializeComponent();
_controller = controller;
_controller.ConfigChanged += (sender, args) => LoadConfiguration();
LoadConfiguration();
Load += (sender, args) => InitData();
}

private void LoadConfiguration()
{
var configs = _controller.GetCurrentConfiguration().configs;
_servers = configs.Select(server => server.FriendlyName()).ToList();
_configuration = _controller.StatisticsConfiguration
?? new StatisticsStrategyConfiguration();
if (_configuration.Calculations == null)
{
_configuration = new StatisticsStrategyConfiguration();
}
}

private void InitData()
{
bindingConfiguration.Add(_configuration);
foreach (var kv in _configuration.Calculations)
{
var calculation = new CalculationControl(kv.Key, kv.Value);
calculationContainer.Controls.Add(calculation);
}

serverSelector.DataSource = _servers;

_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"].YValueMembers = "Package Loss";
StatisticsChart.Series["Ping"].XValueMember = "Timestamp";
StatisticsChart.Series["Ping"].YValueMembers = "Ping";
StatisticsChart.DataSource = _dataTable;
loadChartData();
StatisticsChart.DataBind();
}

private void CancelButton_Click(object sender, EventArgs e)
{
Close();
}

private void OKButton_Click(object sender, EventArgs e)
{
foreach (CalculationControl calculation in calculationContainer.Controls)
{
_configuration.Calculations[calculation.Value] = calculation.Factor;
}
_controller?.SaveStrategyConfigurations(_configuration);
_controller?.UpdateStatisticsConfiguration(StatisticsEnabledCheckBox.Checked);
Close();
}

private void loadChartData()
{
string serverName = _servers[serverSelector.SelectedIndex];
_dataTable.Rows.Clear();

//return directly when no data is usable
if (_controller.availabilityStatistics?.FilteredStatistics == null) return;
List<AvailabilityStatistics.RawStatisticsData> statistics;
if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return;
IEnumerable<IGrouping<int, AvailabilityStatistics.RawStatisticsData>> dataGroups;
if (allMode.Checked)
{
dataGroups = statistics.GroupBy(data => data.Timestamp.DayOfYear);
StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "MM/dd/yyyy";
StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "MM/dd/yyyy";
}
else
{
dataGroups = statistics.GroupBy(data => data.Timestamp.Hour);
StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "HH:00";
StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "HH:00";
}
var finalData = from dataGroup in dataGroups
orderby dataGroup.Key
select new
{
Timestamp = dataGroup.First().Timestamp,
Ping = (int)dataGroup.Average(data => data.RoundtripTime),
PackageLoss = (int)
(dataGroup.Count(data => data.ICMPStatus.Equals(IPStatus.TimedOut.ToString()))
/ (float)dataGroup.Count() * 100)
};
foreach (var data in finalData)
{
_dataTable.Rows.Add(data.Timestamp, data.PackageLoss, data.Ping);
}
StatisticsChart.DataBind();
}

private void serverSelector_SelectedIndexChanged(object sender, EventArgs e)
{
loadChartData();
}

private void chartModeSelector_Enter(object sender, EventArgs e)
{

}

private void dayMode_CheckedChanged(object sender, EventArgs e)
{
loadChartData();
}

private void allMode_CheckedChanged(object sender, EventArgs e)
{
loadChartData();
}
}
}

+ 126
- 0
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="bindingConfiguration.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1, 30</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>63</value>
</metadata>
</root>

+ 17
- 4
shadowsocks-csharp/app.config View File

@@ -1,6 +1,19 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
</startup></configuration>
<supportedRuntime version="v4.0" />
<supportedRuntime version="v2.0.50727" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

+ 1
- 1
shadowsocks-csharp/app.manifest View File

@@ -10,7 +10,7 @@
</trustInfo>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
<dpiAware>True/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

+ 11
- 0
shadowsocks-csharp/packages.config View File

@@ -0,0 +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.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>

+ 84
- 3
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -36,6 +36,8 @@
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
@@ -46,6 +48,7 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
@@ -62,12 +65,57 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<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>
<Private>True</Private>
</Reference>
<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>
<Private>True</Private>
</Reference>
<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>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.VisualBasic" />
<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>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<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>
</Reference>
<Reference Include="System.Net" />
<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>
</Reference>
<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>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Forms.DataVisualization" />
<Reference Include="System.Xaml" />
<Reference Include="System.XML" />
<Reference Include="UIAutomationProvider" />
<Reference Include="WindowsBase" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
<Compile Include="3rd\zxing\BarcodeFormat.cs" />
@@ -116,7 +164,6 @@
<Compile Include="3rd\zxing\qrcode\encoder\MaskUtil.cs" />
<Compile Include="3rd\zxing\qrcode\encoder\MatrixUtil.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\Result.cs" />
<Compile Include="3rd\zxing\ResultMetadataType.cs" />
@@ -125,7 +172,7 @@
<Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Controller\Service\AvailabilityStatistics.cs" />
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" />
<Compile Include="Controller\Strategy\SimplyChooseByStatisticsStrategy.cs" />
<Compile Include="Controller\Strategy\StatisticsStrategy.cs" />
<Compile Include="Controller\System\AutoStartup.cs" />
<Compile Include="Controller\FileManager.cs" />
<Compile Include="Controller\Service\GFWListUpdater.cs" />
@@ -138,15 +185,17 @@
<Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" />
<Compile Include="Encryption\IVEncryptor.cs" />
<Compile Include="Encryption\MbedTLS.cs" />
<Compile Include="Encryption\PolarSSL.cs" />
<Compile Include="Encryption\PolarSSLEncryptor.cs" />
<Compile Include="Encryption\Sodium.cs" />
<Compile Include="Encryption\SodiumEncryptor.cs" />
<Compile Include="Encryption\TableEncryptor.cs" />
<Compile Include="Encryption\IEncryptor.cs" />
<Compile Include="Controller\Service\PACServer.cs" />
<Compile Include="Model\LogViewerConfig.cs" />
<Compile Include="Model\Server.cs" />
<Compile Include="Model\Configuration.cs" />
<Compile Include="Model\StatisticsStrategyConfiguration.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@@ -168,6 +217,12 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Controller\ShadowsocksController.cs" />
<Compile Include="Controller\System\SystemProxy.cs" />
<Compile Include="View\CalculationControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="View\CalculationControl.Designer.cs">
<DependentUpon>CalculationControl.cs</DependentUpon>
</Compile>
<Compile Include="View\LogForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -184,6 +239,12 @@
<Compile Include="View\QRCodeSplashForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="View\StatisticsStrategyConfigurationForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="View\StatisticsStrategyConfigurationForm.Designer.cs">
<DependentUpon>StatisticsStrategyConfigurationForm.cs</DependentUpon>
</Compile>
<EmbeddedResource Include="View\ConfigForm.resx">
<DependentUpon>ConfigForm.cs</DependentUpon>
<SubType>Designer</SubType>
@@ -193,12 +254,18 @@
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="View\CalculationControl.resx">
<DependentUpon>CalculationControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\LogForm.resx">
<DependentUpon>LogForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\QRCodeForm.resx">
<DependentUpon>QRCodeForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\StatisticsStrategyConfigurationForm.resx">
<DependentUpon>StatisticsStrategyConfigurationForm.cs</DependentUpon>
</EmbeddedResource>
<None Include="app.config" />
<None Include="app.manifest">
<SubType>Designer</SubType>
@@ -210,6 +277,8 @@
<None Include="Data\proxy.pac.txt.gz" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Properties\DataSources\Shadowsocks.Model.StatisticsStrategyConfiguration.datasource" />
<None Include="Resources\ss20.png" />
<None Include="Resources\ss16.png" />
<None Include="Resources\ss24.png" />
@@ -217,6 +286,9 @@
<Content Include="Data\cn.txt" />
<Content Include="Data\privoxy_conf.txt" />
<Content Include="Data\user-rule.txt" />
<Content Include="FodyWeavers.xml">
<SubType>Designer</SubType>
</Content>
<Content Include="shadowsocks.ico" />
</ItemGroup>
<ItemGroup>
@@ -252,6 +324,15 @@
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.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('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>
<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.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">


+ 29
- 14
test/UnitTest.cs View File

@@ -13,19 +13,34 @@ namespace test
[TestMethod]
public void TestCompareVersion()
{
Assert.IsTrue(UpdateChecker.CompareVersion("2.3.1.0", "2.3.1") == 0);
Assert.IsTrue(UpdateChecker.CompareVersion("1.2", "1.3") < 0);
Assert.IsTrue(UpdateChecker.CompareVersion("1.3", "1.2") > 0);
Assert.IsTrue(UpdateChecker.CompareVersion("1.3", "1.3") == 0);
Assert.IsTrue(UpdateChecker.CompareVersion("1.2.1", "1.2") > 0);
Assert.IsTrue(UpdateChecker.CompareVersion("2.3.1", "2.4") < 0);
Assert.IsTrue(UpdateChecker.CompareVersion("1.3.2", "1.3.1") > 0);
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("2.3.1.0", "2.3.1") == 0);
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.2", "1.3") < 0);
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3", "1.2") > 0);
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3", "1.3") == 0);
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.2.1", "1.2") > 0);
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("2.3.1", "2.4") < 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)
{
byte[] plain = new byte[16384];
byte[] cipher = new byte[plain.Length + 16];
byte[] cipher = new byte[plain.Length + 16 + IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES];
byte[] plain2 = new byte[plain.Length + 16];
int outLen = 0;
int outLen2 = 0;
@@ -84,8 +99,8 @@ namespace test
{
IEncryptor encryptor;
IEncryptor decryptor;
encryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!");
decryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!");
encryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!", false, false);
decryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!", false, false);
RunEncryptionRound(encryptor, decryptor);
}
}
@@ -124,8 +139,8 @@ namespace test
var random = new Random();
IEncryptor encryptor;
IEncryptor decryptor;
encryptor = new PolarSSLEncryptor("rc4-md5", "barfoo!");
decryptor = new PolarSSLEncryptor("rc4-md5", "barfoo!");
encryptor = new PolarSSLEncryptor("rc4-md5", "barfoo!", false, false);
decryptor = new PolarSSLEncryptor("rc4-md5", "barfoo!", false, false);
RunEncryptionRound(encryptor, decryptor);
}
}
@@ -164,8 +179,8 @@ namespace test
var random = new Random();
IEncryptor encryptor;
IEncryptor decryptor;
encryptor = new SodiumEncryptor("salsa20", "barfoo!");
decryptor = new SodiumEncryptor("salsa20", "barfoo!");
encryptor = new SodiumEncryptor("salsa20", "barfoo!", false, false);
decryptor = new SodiumEncryptor("salsa20", "barfoo!", false, false);
RunEncryptionRound(encryptor, decryptor);
}
}


+ 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>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<OutputPath>bin\x86\Debug\</OutputPath>
@@ -30,7 +32,30 @@
<StartupObject />
</PropertyGroup>
<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.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>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
@@ -54,6 +79,9 @@
<Name>shadowsocks-csharp</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
@@ -74,6 +102,13 @@
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.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.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">


Loading…
Cancel
Save