Browse Source

Merge pull request #413 from kimw/master

a lot of commits
tags/3.0
Gang Zhuo 9 years ago
parent
commit
d2833e6d9f
31 changed files with 729 additions and 2330 deletions
  1. +0
    -1902
      shadowsocks-csharp/3rd/SimpleJson.cs
  2. +47
    -10
      shadowsocks-csharp/Controller/Logging.cs
  3. +44
    -37
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  4. +17
    -20
      shadowsocks-csharp/Controller/Service/GfwListUpdater.cs
  5. +7
    -9
      shadowsocks-csharp/Controller/Service/Listener.cs
  6. +92
    -41
      shadowsocks-csharp/Controller/Service/PACServer.cs
  7. +14
    -20
      shadowsocks-csharp/Controller/Service/PolipoRunner.cs
  8. +0
    -3
      shadowsocks-csharp/Controller/Service/PortForwarder.cs
  9. +92
    -94
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  10. +14
    -8
      shadowsocks-csharp/Controller/Service/UDPRelay.cs
  11. +20
    -23
      shadowsocks-csharp/Controller/Service/UpdateChecker.cs
  12. +65
    -6
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  13. +5
    -5
      shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs
  14. +2
    -1
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
  15. +8
    -11
      shadowsocks-csharp/Encryption/MbedTLS.cs
  16. +7
    -10
      shadowsocks-csharp/Encryption/PolarSSL.cs
  17. +7
    -10
      shadowsocks-csharp/Encryption/Sodium.cs
  18. +7
    -21
      shadowsocks-csharp/Model/Configuration.cs
  19. +31
    -8
      shadowsocks-csharp/Model/LogViewerConfig.cs
  20. +2
    -5
      shadowsocks-csharp/Model/Server.cs
  21. +7
    -8
      shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs
  22. +14
    -10
      shadowsocks-csharp/Program.cs
  23. +56
    -13
      shadowsocks-csharp/Util/Util.cs
  24. +12
    -11
      shadowsocks-csharp/View/LogForm.Designer.cs
  25. +35
    -19
      shadowsocks-csharp/View/LogForm.cs
  26. +24
    -0
      shadowsocks-csharp/View/LogForm.resx
  27. +56
    -12
      shadowsocks-csharp/View/MenuViewController.cs
  28. +6
    -8
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs
  29. +1
    -1
      shadowsocks-csharp/shadowsocks-csharp.csproj
  30. +7
    -4
      test/packages.config
  31. +30
    -0
      test/test.csproj

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


+ 47
- 10
shadowsocks-csharp/Controller/Logging.cs View File

@@ -1,9 +1,9 @@
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
{
@@ -15,8 +15,7 @@ namespace Shadowsocks.Controller
{
try
{
string temppath = Utils.GetTempPath();
LogFile = Path.Combine(temppath, "shadowsocks.log");
LogFile = Utils.GetTempPath("shadowsocks.log");
FileStream fs = new FileStream(LogFile, FileMode.Append);
StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs);
sw.AutoFlush = true;
@@ -32,11 +31,41 @@ namespace Shadowsocks.Controller
}
}
public static void Error(object o)
{
Console.WriteLine("[E] " + o);
}
public static void Info(object o)
{
Console.WriteLine(o);
}
public static void Debug(object o)
{
#if DEBUG
Console.WriteLine("[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 +86,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 +106,7 @@ namespace Shadowsocks.Controller
}
else
{
Console.WriteLine(e);
Info(e);
}
}
}


+ 44
- 37
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -9,6 +9,10 @@ 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 Shadowsocks.Util;

@@ -30,8 +34,8 @@ namespace Shadowsocks.Controller
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 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;
@@ -42,8 +46,7 @@ namespace Shadowsocks.Controller
//static constructor to initialize every public static fields before refereced
static AvailabilityStatistics()
{
var temppath = Utils.GetTempPath();
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName);
AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName);
}

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

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

}
}

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

@@ -1,12 +1,14 @@
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
{
@@ -14,12 +16,6 @@ namespace Shadowsocks.Controller
{
private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt";
private static string PAC_FILE = PACServer.PAC_FILE;
private static string USER_RULE_FILE = PACServer.USER_RULE_FILE;
private static string USER_ABP_FILE = PACServer.USER_ABP_FILE;
public event EventHandler<ResultEventArgs> UpdateCompleted;
public event ErrorEventHandler Error;
@@ -38,12 +34,13 @@ 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;
@@ -51,25 +48,25 @@ namespace Shadowsocks.Controller
}
}
string abpContent;
if (File.Exists(USER_ABP_FILE))
if (File.Exists(PACServer.USER_ABP_FILE))
{
abpContent = File.ReadAllText(USER_ABP_FILE, Encoding.UTF8);
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
}
else
{
abpContent = Utils.UnGzip(Resources.abp_js);
}
abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines));
if (File.Exists(PAC_FILE))
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));
@@ -92,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);


+ 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();
}
}


+ 92
- 41
shadowsocks-csharp/Controller/Service/PACServer.cs View File

@@ -1,33 +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";
public static 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)
@@ -48,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")
@@ -58,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)
{
@@ -144,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);
}
catch (Exception e)
{
Console.WriteLine(e);
Logging.LogUsefulException(e);
socket.Close();
}
}
@@ -169,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 WatchUserRuleFile()
{
if (UserRuleFileWatcher != null)
{
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;
}
private void Watcher_Changed(object sender, FileSystemEventArgs e)
#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)
{
if (PACFileChanged != null)
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)
{
PACFileChanged(this, new EventArgs());
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)
{
@@ -204,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();


+ 92
- 94
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;
@@ -117,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()
{
@@ -125,23 +140,23 @@ namespace Shadowsocks.Controller
{
throw new ArgumentException("No server configured");
}
this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
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();
}
}
@@ -149,7 +164,6 @@ namespace Shadowsocks.Controller
{
lock (relay.Handlers)
{
Logging.Debug($"connections: {relay.Handlers.Count}");
relay.Handlers.Remove(this);
}
lock (this)
@@ -213,20 +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");
}
Logging.Debug($"======Send Local Port, size:" + response.Length);
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null);
}
else
{
this.Close();
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -240,21 +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
Logging.Debug($"======Receive Local Port, size:" + 3);
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();
}
}
@@ -274,7 +285,6 @@ namespace Shadowsocks.Controller
if (command == 1)
{
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
Logging.Debug($"======Send Local Port, size:" + response.Length);
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null);
}
else if (command == 3)
@@ -284,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();
}
}
@@ -313,7 +323,6 @@ namespace Shadowsocks.Controller
address.CopyTo(response, 4);
response[response.Length - 1] = (byte)(port & 0xFF);
response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
Logging.Debug($"======Send Local Port, size:" + response.Length);
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ReadAll), true);
}
@@ -328,29 +337,27 @@ namespace Shadowsocks.Controller
if (ar.AsyncState != null)
{
connection.EndSend(ar);
Logging.Debug($"======Receive Local Port, size:" + RecvSize);
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)
{
Logging.Debug($"======Receive Local Port, size:" + RecvSize);
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();
}
}
@@ -366,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)
{
}
}
@@ -408,14 +416,12 @@ namespace Shadowsocks.Controller
connected = false;
// Connect to the remote endpoint.
Logging.Debug($"++++++Connect Server Port");
remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), connectTimer);
remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -431,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();
}
}
@@ -470,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();
@@ -508,17 +513,13 @@ namespace Shadowsocks.Controller
}
try
{
Logging.Debug($"++++++Receive Server Port, size:" + RecvSize);
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
Logging.Debug($"======Receive Local Port, size:"+ RecvSize);
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();
}
}
@@ -532,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)
{
@@ -545,34 +547,33 @@ namespace Shadowsocks.Controller
}
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend);
}
Logging.Debug($"======Send Local Port, size:" + 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();
}
}
@@ -586,6 +587,7 @@ namespace Shadowsocks.Controller
{
int bytesRead = connection.EndReceive(ar);
totalWrite += bytesRead;
tcprelay.UpdateOutboundCounter(bytesRead);
if (bytesRead > 0)
{
@@ -598,13 +600,13 @@ namespace Shadowsocks.Controller
}
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend);
}
Logging.Debug($"++++++Send Server Port, size:" + bytesToSend);
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)");
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
@@ -617,7 +619,7 @@ namespace Shadowsocks.Controller
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
Close();
}
}
@@ -630,14 +632,12 @@ namespace Shadowsocks.Controller
try
{
remote.EndSend(ar);
Logging.Debug($"======Receive Local Port, size:" + RecvSize);
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();
}
}
@@ -650,14 +650,12 @@ namespace Shadowsocks.Controller
try
{
connection.EndSend(ar);
Logging.Debug($"++++++Receive Server Port, size:" + RecvSize);
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();
}
}
}


+ 14
- 8
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,8 +74,8 @@ 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, _server.auth, true);
@@ -80,15 +84,17 @@ namespace Shadowsocks.Controller
byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES];
int outlen;
encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen);
Logging.Debug($"++++++Send Server Port, size:" + 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
@@ -105,7 +111,7 @@ namespace Shadowsocks.Controller
byte[] sendBuf = new byte[outlen + 3];
Array.Copy(dataOut, 0, sendBuf, 3, outlen);
Logging.Debug($"======Send Local Port, size:" + (outlen + 3));
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
_local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint);
Receive();
}
@@ -121,6 +127,7 @@ namespace Shadowsocks.Controller
{
}
}
public void Close()
{
try
@@ -142,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
{


+ 20
- 23
shadowsocks-csharp/Controller/Service/UpdateChecker.cs View File

@@ -1,12 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using SimpleJson;
using Newtonsoft.Json.Linq;
using Shadowsocks.Model;
using Shadowsocks.Util;
@@ -26,7 +23,7 @@ namespace Shadowsocks.Controller
public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted;
public const string Version = "2.5.8";
public const string Version = "2.5.8.1";
private class CheckUpdateTimer : System.Timers.Timer
{
@@ -79,26 +76,28 @@ namespace Shadowsocks.Controller
{
string response = e.Result;
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
JArray result = JArray.Parse(response);
List<Asset> asserts = new List<Asset>();
foreach (JsonObject release in result)
if (result != null)
{
if ((bool)release["prerelease"])
{
continue;
}
foreach (JsonObject asset in (JsonArray)release["assets"])
foreach (JObject release in result)
{
Asset ass = new Asset();
ass.Parse(asset);
if (ass.IsNewVersion(Version))
if ((bool)release["prerelease"])
{
asserts.Add(ass);
continue;
}
foreach (JObject asset in (JArray)release["assets"])
{
Asset ass = new Asset();
ass.Parse(asset);
if (ass.IsNewVersion(Version))
{
asserts.Add(ass);
}
}
}
}
if (asserts.Count != 0)
{
SortByVersions(asserts);
@@ -129,8 +128,7 @@ namespace Shadowsocks.Controller
{
try
{
string temppath = Utils.GetTempPath();
LatestVersionLocalName = Path.Combine(temppath, LatestVersionName);
LatestVersionLocalName = Utils.GetTempPath(LatestVersionName);
WebClient http = CreateWebClient();
http.DownloadFileCompleted += Http_DownloadFileCompleted;
http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName);
@@ -145,7 +143,7 @@ namespace Shadowsocks.Controller
{
try
{
if(e.Error != null)
if (e.Error != null)
{
Logging.LogUsefulException(e.Error);
return;
@@ -195,7 +193,7 @@ namespace Shadowsocks.Controller
return CompareVersion(version, currentVersion) > 0;
}
public void Parse(JsonObject asset)
public void Parse(JObject asset)
{
name = (string)asset["name"];
browser_download_url = (string)asset["browser_download_url"];
@@ -241,6 +239,5 @@ namespace Shadowsocks.Controller
return Asset.CompareVersion(x.version, y.version);
}
}
}
}

+ 65
- 6
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
{
@@ -28,6 +33,9 @@ namespace Shadowsocks.Controller
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;
@@ -60,7 +68,6 @@ namespace Shadowsocks.Controller
StartReleasingMemory();
}
public void Start()
{
Reload();
@@ -300,6 +307,16 @@ namespace Shadowsocks.Controller
Configuration.Save(_config);
}
public void UpdateInboundCounter(long n)
{
inboundCounter += n;
}
public void UpdateOutboundCounter(long n)
{
outboundCounter += n;
}
protected void Reload()
{
// some logic in configuration updated the config when saving, we need to read it again
@@ -314,6 +331,7 @@ namespace Shadowsocks.Controller
{
_pacServer = new PACServer();
_pacServer.PACFileChanged += pacServer_PACFileChanged;
_pacServer.UserRuleFileChanged += pacServer_UserRuleFileChanged;
}
_pacServer.UpdateConfiguration(_config);
if (gfwListUpdater == null)
@@ -424,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));


+ 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))


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

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

using Newtonsoft.Json;

using Shadowsocks.Model;

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

}
}

+ 8
- 11
shadowsocks-csharp/Encryption/MbedTLS.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,13 +14,12 @@ namespace Shadowsocks.Encryption

static MbedTLS()
{
string tempPath = Utils.GetTempPath();
string dllPath = tempPath + "/libsscrypto.dll";
string dllPath = Utils.GetTempPath("libsscrypto.dll");
try
{
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
}
catch (IOException ex)
catch (IOException)
{
}
catch (Exception e)
@@ -59,10 +57,9 @@ namespace Shadowsocks.Encryption
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 );
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);
}
}

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

@@ -1,11 +1,10 @@
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Encryption
{
@@ -15,8 +14,7 @@ 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);
@@ -26,7 +24,7 @@ namespace Shadowsocks.Encryption
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Logging.LogUsefulException(e);
}
LoadLibrary(dllPath);
}
@@ -47,7 +45,6 @@ namespace Shadowsocks.Encryption
public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen,
byte[] input, int ioff, uint ilen,
byte[] output);
}
}

+ 7
- 21
shadowsocks-csharp/Model/Configuration.cs View File

@@ -1,10 +1,9 @@
using Shadowsocks.Controller;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using SimpleJson;
using Shadowsocks.Controller;
using Newtonsoft.Json;
namespace Shadowsocks.Model
{
@@ -53,7 +52,7 @@ 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)
{
@@ -72,7 +71,7 @@ namespace Shadowsocks.Model
{
if (!(e is FileNotFoundException))
{
Console.WriteLine(e);
Logging.LogUsefulException(e);
}
return new Configuration
{
@@ -110,7 +109,7 @@ namespace Shadowsocks.Model
{
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();
}
@@ -166,18 +165,5 @@ namespace Shadowsocks.Model
throw new ArgumentException(I18N.GetString("Server IP can not be blank"));
}
}
private class JsonSerializerStrategy : SimpleJson.PocoJsonSerializerStrategy
{
// convert string to int
public override object DeserializeObject(object value, Type type)
{
if (type == typeof(Int32) && value.GetType() == typeof(string))
{
return Int32.Parse(value.ToString());
}
return base.DeserializeObject(value, type);
}
}
}
}

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

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


+ 2
- 5
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]


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

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

using Newtonsoft.Json;

using Shadowsocks.Controller;

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


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

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

public StatisticsStrategyConfiguration()
{
var availabilityStatisticsType = typeof (AvailabilityStatistics);
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);
Calculations = properties.ToDictionary(p => p.Name, _ => (float)0);
}

public bool StatisticsEnabled


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

@@ -1,13 +1,13 @@
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.View;
using System;
using System.Collections.Generic;
using System;
using System.Diagnostics;
using System.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
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();
}
}


+ 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,


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

@@ -57,13 +57,13 @@
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, 38);
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(584, 377);
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 131);
this.LogMessageTextBox.TabIndex = 0;
//
// MainMenu
@@ -144,9 +144,9 @@
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(249, 3);
this.TopMostCheckBox.Location = new System.Drawing.Point(247, 3);
this.TopMostCheckBox.Name = "TopMostCheckBox";
this.TopMostCheckBox.Size = new System.Drawing.Size(72, 23);
this.TopMostCheckBox.Size = new System.Drawing.Size(71, 25);
this.TopMostCheckBox.TabIndex = 3;
this.TopMostCheckBox.Text = "&Top Most";
this.TopMostCheckBox.UseVisualStyleBackColor = true;
@@ -157,7 +157,7 @@
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;
@@ -168,7 +168,7 @@
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.UseVisualStyleBackColor = true;
@@ -181,7 +181,7 @@
this.WrapTextCheckBox.AutoSize = true;
this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3);
this.WrapTextCheckBox.Name = "WrapTextCheckBox";
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 23);
this.WrapTextCheckBox.Size = new System.Drawing.Size(76, 25);
this.WrapTextCheckBox.TabIndex = 0;
this.WrapTextCheckBox.Text = "&Wrap Text";
this.WrapTextCheckBox.UseVisualStyleBackColor = true;
@@ -199,7 +199,7 @@
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(590, 418);
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 174);
this.tableLayoutPanel1.TabIndex = 2;
//
// ToolbarFlowLayoutPanel
@@ -212,16 +212,17 @@
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(584, 29);
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(590, 418);
this.ClientSize = new System.Drawing.Size(384, 174);
this.Controls.Add(this.tableLayoutPanel1);
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";


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

@@ -1,16 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
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
{
@@ -27,17 +23,21 @@ namespace Shadowsocks.View
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();
topMostTrigger = config.topMost;
wrapTextTrigger = config.wrapText;
toolbarTrigger = config.toolbarShown;
LogMessageTextBox.BackColor = config.GetBackgroundColor();
LogMessageTextBox.ForeColor = config.GetTextColor();
LogMessageTextBox.Font = config.GetFont();
}
else {
topMostTrigger = config.topMost;
wrapTextTrigger = config.wrapText;
toolbarTrigger = config.toolbarShown;
LogMessageTextBox.BackColor = config.GetBackgroundColor();
LogMessageTextBox.ForeColor = config.GetTextColor();
LogMessageTextBox.Font = config.GetFont();
}
UpdateTexts();
}
@@ -57,7 +57,7 @@ namespace Shadowsocks.View
WrapTextMenuItem.Text = I18N.GetString("&Wrap Text");
TopMostMenuItem.Text = I18N.GetString("&Top Most");
ShowToolbarMenuItem.Text = I18N.GetString("&Show Toolbar");
this.Text = I18N.GetString("Log Viewer");
Text = I18N.GetString("Log Viewer");
}
private void Timer_Tick(object sender, EventArgs e)
@@ -108,18 +108,30 @@ 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;
this.TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
topMostTriggerLock = false;
wrapTextTriggerLock = true;
@@ -141,19 +153,23 @@ namespace Shadowsocks.View
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)
@@ -208,7 +224,7 @@ namespace Shadowsocks.View
}
#endregion
#region Trigger the log messages wrapable, or not.
#region Trigger the log messages to wrapable, or not.
bool wrapTextTrigger = false;
bool wrapTextTriggerLock = false;
@@ -241,7 +257,7 @@ namespace Shadowsocks.View
}
#endregion
#region Trigger this window top most, or not.
#region Trigger the window to top most, or not.
bool topMostTrigger = false;
bool topMostTriggerLock = false;
@@ -250,7 +266,7 @@ namespace Shadowsocks.View
topMostTriggerLock = true;
topMostTrigger = !topMostTrigger;
this.TopMost = topMostTrigger;
TopMost = topMostTrigger;
TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
topMostTriggerLock = false;


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

@@ -117,7 +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="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="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>

+ 56
- 12
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -1,16 +1,17 @@
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;
namespace Shadowsocks.View
{
public class MenuViewController
@@ -44,6 +45,8 @@ namespace Shadowsocks.View
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)
@@ -67,6 +70,7 @@ namespace Shadowsocks.View
_notifyIcon.Visible = true;
_notifyIcon.ContextMenu = contextMenu1;
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
_notifyIcon.MouseClick += notifyIcon1_Click;
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
@@ -202,7 +206,6 @@ namespace Shadowsocks.View
});
}
private void controller_ConfigChanged(object sender, EventArgs e)
{
LoadCurrentConfiguration();
@@ -289,7 +292,6 @@ namespace Shadowsocks.View
}
}
private void LoadCurrentConfiguration()
{
Configuration config = controller.GetConfigurationCopy();
@@ -357,6 +359,32 @@ namespace Shadowsocks.View
}
}
private void ShowLogForms()
{
if (logForms.Count == 0)
{
LogForm f = new LogForm(controller, Logging.LogFile);
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;
@@ -393,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)
@@ -451,11 +491,13 @@ namespace Shadowsocks.View
private void ShowLogItem_Click(object sender, EventArgs e)
{
string argument = Logging.LogFile;
LogForm f = new LogForm(controller, Logging.LogFile);
f.Show();
f.FormClosed += logForm_FormClosed;
new LogForm(controller, argument).Show();
logForms.Add(f);
}
private void StatisticsConfigItem_Click(object sender, EventArgs e)
{
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller);
@@ -564,9 +606,11 @@ 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"));
}
}


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

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

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

serverSelector.DataSource = _servers;

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

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


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


+ 1
- 1
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -48,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>
@@ -169,7 +170,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" />


+ 7
- 4
test/packages.config View File

@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
<package id="System.Net.Http" version="2.0.20710.0" targetFramework="net45" />
<?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>

+ 30
- 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,8 +32,29 @@
<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>
@@ -79,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