@@ -1,3 +1,4 @@ | |||
/.vs/ | |||
Backup/ | |||
bin/ | |||
obj/ | |||
@@ -1,3 +1,4 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<configuration> | |||
<config> | |||
<add key="repositoryPath" value="shadowsocks-csharp\3rd" /> | |||
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} | |||
@@ -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}\""), | |||
@@ -123,11 +133,10 @@ namespace Shadowsocks.Controller | |||
{ | |||
Logging.Debug("Ping " + server.FriendlyName()); | |||
if (server.server == "") return null; | |||
var IP = Dns.GetHostAddresses(server.server).First(ip => ip.AddressFamily == AddressFamily.InterNetwork); | |||
var IP = Dns.GetHostAddresses(server.server).First(ip => (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6)); | |||
var ping = new Ping(); | |||
var 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; | |||
} | |||
} | |||
} |
@@ -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,10 +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; | |||
public event EventHandler<ResultEventArgs> UpdateCompleted; | |||
public event ErrorEventHandler Error; | |||
@@ -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); | |||
@@ -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(); | |||
} | |||
} | |||
@@ -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 + ";"; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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(); | |||
@@ -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.one_time_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,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(); | |||
} | |||
} | |||
@@ -239,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(); | |||
} | |||
} | |||
@@ -281,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(); | |||
} | |||
} | |||
@@ -324,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(); | |||
} | |||
} | |||
@@ -360,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) | |||
{ | |||
} | |||
} | |||
@@ -402,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(); | |||
} | |||
} | |||
@@ -424,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(); | |||
} | |||
} | |||
@@ -463,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(); | |||
@@ -501,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(); | |||
} | |||
} | |||
@@ -523,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) | |||
{ | |||
@@ -536,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(); | |||
} | |||
} | |||
@@ -588,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 | |||
@@ -606,7 +619,7 @@ namespace Shadowsocks.Controller | |||
catch (Exception e) | |||
{ | |||
Logging.LogUsefulException(e); | |||
this.Close(); | |||
Close(); | |||
} | |||
} | |||
@@ -619,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(); | |||
} | |||
} | |||
@@ -638,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(); | |||
} | |||
} | |||
} | |||
@@ -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, _server.one_time_auth, true); | |||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true); | |||
byte[] dataIn = new byte[length - 3 + IVEncryptor.ONETIMEAUTH_BYTES]; | |||
Array.Copy(data, 3, dataIn, 0, length - 3); | |||
byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES]; | |||
int outlen; | |||
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, _server.one_time_auth, true); | |||
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 | |||
{ | |||
@@ -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,35 @@ namespace Shadowsocks.Controller | |||
public string LatestVersionLocalName; | |||
public event EventHandler CheckUpdateCompleted; | |||
public const string Version = "2.5.8"; | |||
public const string Version = "2.5.8.2"; | |||
private class CheckUpdateTimer : System.Timers.Timer | |||
{ | |||
public Configuration config; | |||
public CheckUpdateTimer(int p) : base(p) | |||
{ | |||
} | |||
} | |||
public void CheckUpdate(Configuration config, int delay) | |||
{ | |||
CheckUpdateTimer timer = new CheckUpdateTimer(delay); | |||
timer.AutoReset = false; | |||
timer.Elapsed += Timer_Elapsed; | |||
timer.config = config; | |||
timer.Enabled = true; | |||
} | |||
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) | |||
{ | |||
CheckUpdateTimer timer = (CheckUpdateTimer)sender; | |||
Configuration config = timer.config; | |||
timer.Elapsed -= Timer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
CheckUpdate(config); | |||
} | |||
public void CheckUpdate(Configuration config) | |||
{ | |||
@@ -34,6 +59,7 @@ namespace Shadowsocks.Controller | |||
try | |||
{ | |||
Logging.Debug("Checking updates..."); | |||
WebClient http = CreateWebClient(); | |||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||
http.DownloadStringAsync(new Uri(UpdateURL)); | |||
@@ -50,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"]) | |||
{ | |||
continue; | |||
} | |||
foreach (JObject asset in (JArray)release["assets"]) | |||
{ | |||
asserts.Add(ass); | |||
Asset ass = new Asset(); | |||
ass.Parse(asset); | |||
if (ass.IsNewVersion(Version)) | |||
{ | |||
asserts.Add(ass); | |||
} | |||
} | |||
} | |||
} | |||
if (asserts.Count != 0) | |||
{ | |||
SortByVersions(asserts); | |||
@@ -81,9 +109,13 @@ namespace Shadowsocks.Controller | |||
startDownload(); | |||
} | |||
else if (CheckUpdateCompleted != null) | |||
else | |||
{ | |||
CheckUpdateCompleted(this, new EventArgs()); | |||
Logging.Debug("No update is available"); | |||
if (CheckUpdateCompleted != null) | |||
{ | |||
CheckUpdateCompleted(this, new EventArgs()); | |||
} | |||
} | |||
} | |||
catch (Exception ex) | |||
@@ -96,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); | |||
@@ -112,11 +143,12 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
if(e.Error != null) | |||
if (e.Error != null) | |||
{ | |||
Logging.LogUsefulException(e.Error); | |||
return; | |||
} | |||
Logging.Debug($"New version {LatestVersionNumber} found: {LatestVersionLocalName}"); | |||
if (CheckUpdateCompleted != null) | |||
{ | |||
CheckUpdateCompleted(this, new EventArgs()); | |||
@@ -161,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"]; | |||
@@ -207,6 +239,5 @@ namespace Shadowsocks.Controller | |||
return Asset.CompareVersion(x.version, y.version); | |||
} | |||
} | |||
} | |||
} |
@@ -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(); | |||
@@ -246,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; | |||
} | |||
@@ -300,6 +307,16 @@ namespace Shadowsocks.Controller | |||
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 | |||
@@ -314,6 +331,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
_pacServer = new PACServer(); | |||
_pacServer.PACFileChanged += pacServer_PACFileChanged; | |||
_pacServer.UserRuleFileChanged += pacServer_UserRuleFileChanged; | |||
} | |||
_pacServer.UpdateConfiguration(_config); | |||
if (gfwListUpdater == null) | |||
@@ -380,7 +398,7 @@ namespace Shadowsocks.Controller | |||
} | |||
UpdateSystemProxy(); | |||
Util.Utils.ReleaseMemory(true); | |||
Utils.ReleaseMemory(true); | |||
} | |||
protected void SaveConfig(Configuration newConfig) | |||
@@ -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)); | |||
@@ -435,7 +494,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
while (true) | |||
{ | |||
Util.Utils.ReleaseMemory(false); | |||
Utils.ReleaseMemory(false); | |||
Thread.Sleep(30 * 1000); | |||
} | |||
} | |||
@@ -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)) | |||
@@ -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 | |||
} | |||
} | |||
} |
@@ -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); } | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -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 |
@@ -24,7 +24,7 @@ namespace Shadowsocks.Encryption | |||
protected byte[] GetPasswordHash() | |||
{ | |||
byte[] inputBytes = Encoding.UTF8.GetBytes(Password); | |||
byte[] hash = MD5.Create().ComputeHash(inputBytes); | |||
byte[] hash = MbedTLS.MD5(inputBytes); | |||
return hash; | |||
} | |||
@@ -13,10 +13,6 @@ namespace Shadowsocks.Encryption | |||
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)); | |||
@@ -31,7 +27,7 @@ namespace Shadowsocks.Encryption | |||
{ | |||
if (string.IsNullOrEmpty(method)) | |||
{ | |||
method = "table"; | |||
method = "aes-256-cfb"; | |||
} | |||
method = method.ToLowerInvariant(); | |||
Type t = _registeredEncryptors[method]; | |||
@@ -62,18 +62,22 @@ namespace Shadowsocks.Encryption | |||
} | |||
keyLen = ciphers[_method][0]; | |||
ivLen = ciphers[_method][1]; | |||
if (CachedKeys.ContainsKey(k)) | |||
if (!CachedKeys.ContainsKey(k)) | |||
{ | |||
_key = CachedKeys[k]; | |||
} | |||
else | |||
{ | |||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | |||
_key = new byte[32]; | |||
byte[] iv = new byte[16]; | |||
bytesToKey(passbuf, _key); | |||
CachedKeys[k] = _key; | |||
lock (CachedKeys) | |||
{ | |||
if (!CachedKeys.ContainsKey(k)) | |||
{ | |||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | |||
_key = new byte[32]; | |||
byte[] iv = new byte[16]; | |||
bytesToKey(passbuf, _key); | |||
CachedKeys[k] = _key; | |||
} | |||
} | |||
} | |||
if (_key == null) | |||
_key = CachedKeys[k]; | |||
} | |||
protected void bytesToKey(byte[] password, byte[] key) | |||
@@ -83,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; | |||
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 | |||
{ | |||
@@ -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); | |||
} | |||
@@ -40,11 +38,13 @@ namespace Shadowsocks.Encryption | |||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | |||
public extern static int crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); | |||
[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); | |||
} | |||
} | |||
@@ -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; | |||
@@ -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; | |||
@@ -1,106 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
namespace Shadowsocks.Encryption | |||
{ | |||
public class TableEncryptor | |||
: EncryptorBase | |||
{ | |||
public TableEncryptor(string method, string password, bool onetimeauth, bool isudp) | |||
: base(method, password, onetimeauth, isudp) | |||
{ | |||
byte[] hash = GetPasswordHash(); | |||
// TODO endian | |||
ulong a = BitConverter.ToUInt64(hash, 0); | |||
for (int i = 0; i < 256; i++) | |||
{ | |||
_encryptTable[i] = (byte)i; | |||
} | |||
for (int i = 1; i < 1024; i++) | |||
{ | |||
_encryptTable = MergeSort(_encryptTable, a, i); | |||
} | |||
for (int i = 0; i < 256; i++) | |||
{ | |||
_decryptTable[_encryptTable[i]] = (byte)i; | |||
} | |||
} | |||
public static List<string> SupportedCiphers() | |||
{ | |||
return new List<string>(new string[]{"table"}); | |||
} | |||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
byte[] result = new byte[length]; | |||
for (int i = 0; i < length; i++) | |||
{ | |||
outbuf[i] = _encryptTable[buf[i]]; | |||
} | |||
outlength = length; | |||
} | |||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||
{ | |||
byte[] result = new byte[length]; | |||
for (int i = 0; i < length; i++) | |||
{ | |||
outbuf[i] = _decryptTable[buf[i]]; | |||
} | |||
outlength = length; | |||
} | |||
private readonly byte[] _encryptTable = new byte[256]; | |||
private readonly byte[] _decryptTable = new byte[256]; | |||
private static long Compare(byte x, byte y, ulong a, int i) | |||
{ | |||
return (long)(a % (ulong)(x + i)) - (long)(a % (ulong)(y + i)); | |||
} | |||
private byte[] MergeSort(byte[] array, ulong a, int j) | |||
{ | |||
if (array.Length == 1) | |||
{ | |||
return array; | |||
} | |||
int middle = array.Length / 2; | |||
byte[] left = new byte[middle]; | |||
for (int i = 0; i < middle; i++) | |||
{ | |||
left[i] = array[i]; | |||
} | |||
byte[] right = new byte[array.Length - middle]; | |||
for (int i = 0; i < array.Length - middle; i++) | |||
{ | |||
right[i] = array[i + middle]; | |||
} | |||
left = MergeSort(left, a, j); | |||
right = MergeSort(right, a, j); | |||
int leftptr = 0; | |||
int rightptr = 0; | |||
byte[] sorted = new byte[array.Length]; | |||
for (int k = 0; k < array.Length; k++) | |||
{ | |||
if (rightptr == right.Length || ((leftptr < left.Length) && (Compare(left[leftptr], right[rightptr], a, j) <= 0))) | |||
{ | |||
sorted[k] = left[leftptr]; | |||
leftptr++; | |||
} | |||
else if (leftptr == left.Length || ((rightptr < right.Length) && (Compare(right[rightptr], left[leftptr], a, j)) <= 0)) | |||
{ | |||
sorted[k] = right[rightptr]; | |||
rightptr++; | |||
} | |||
} | |||
return sorted; | |||
} | |||
public override void Dispose() | |||
{ | |||
} | |||
} | |||
} |
@@ -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 | |||
{ | |||
@@ -32,13 +31,9 @@ namespace Shadowsocks.Model | |||
public Server GetCurrentServer() | |||
{ | |||
if (index >= 0 && index < configs.Count) | |||
{ | |||
return configs[index]; | |||
} | |||
else | |||
{ | |||
return GetDefaultServer(); | |||
} | |||
} | |||
public static void CheckServer(Server server) | |||
@@ -53,27 +48,18 @@ 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, | |||
@@ -91,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(); | |||
} | |||
@@ -129,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); | |||
} | |||
} | |||
} | |||
} |
@@ -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() | |||
@@ -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,7 +14,7 @@ namespace Shadowsocks.Model | |||
public string password; | |||
public string method; | |||
public string remarks; | |||
public bool one_time_auth; | |||
public bool auth; | |||
public override int GetHashCode() | |||
{ | |||
@@ -27,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() | |||
@@ -48,12 +45,12 @@ namespace Shadowsocks.Model | |||
public Server() | |||
{ | |||
this.server = ""; | |||
this.server_port = 8388; | |||
this.method = "aes-256-cfb"; | |||
this.password = ""; | |||
this.remarks = ""; | |||
this.one_time_auth = false; | |||
server = ""; | |||
server_port = 8388; | |||
method = "aes-256-cfb"; | |||
password = ""; | |||
remarks = ""; | |||
auth = false; | |||
} | |||
public Server(string ssURL) : this() | |||
@@ -65,7 +62,7 @@ namespace Shadowsocks.Model | |||
{ | |||
try | |||
{ | |||
bytes = System.Convert.FromBase64String(base64); | |||
bytes = Convert.FromBase64String(base64); | |||
} | |||
catch (FormatException) | |||
{ | |||
@@ -83,16 +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) | |||
{ | |||
@@ -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 | |||
@@ -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(); | |||
} | |||
} | |||
@@ -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, | |||
@@ -198,14 +198,13 @@ | |||
this.EncryptionSelect.ImeMode = System.Windows.Forms.ImeMode.NoControl; | |||
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"}); | |||
"aes-128-cfb"}); | |||
this.EncryptionSelect.Location = new System.Drawing.Point(83, 87); | |||
this.EncryptionSelect.Name = "EncryptionSelect"; | |||
this.EncryptionSelect.Size = new System.Drawing.Size(160, 20); | |||
@@ -79,12 +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, | |||
one_time_auth = OneTimeAuth.Checked | |||
auth = OneTimeAuth.Checked | |||
}; | |||
int localPort = int.Parse(ProxyPortTextBox.Text); | |||
Configuration.CheckServer(server); | |||
@@ -117,7 +117,7 @@ namespace Shadowsocks.View | |||
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); | |||
EncryptionSelect.Text = server.method ?? "aes-256-cfb"; | |||
RemarksTextBox.Text = server.remarks; | |||
OneTimeAuth.Checked = server.one_time_auth; | |||
OneTimeAuth.Checked = server.auth; | |||
} | |||
} | |||
@@ -188,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; | |||
@@ -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"; | |||
@@ -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; | |||
@@ -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> |
@@ -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 | |||
@@ -44,6 +46,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) | |||
@@ -66,7 +70,10 @@ 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.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; | |||
@@ -78,7 +85,7 @@ namespace Shadowsocks.View | |||
if (config.autoCheckUpdate) | |||
{ | |||
_isStartupChecking = true; | |||
updateChecker.CheckUpdate(config); | |||
updateChecker.CheckUpdate(config, 3000); | |||
} | |||
if (config.isDefault) | |||
@@ -200,7 +207,6 @@ namespace Shadowsocks.View | |||
}); | |||
} | |||
private void controller_ConfigChanged(object sender, EventArgs e) | |||
{ | |||
LoadCurrentConfiguration(); | |||
@@ -256,7 +262,6 @@ namespace Shadowsocks.View | |||
if (updateChecker.NewVersionFound) | |||
{ | |||
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000); | |||
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked; | |||
_isFirstRun = false; | |||
} | |||
else if (!_isStartupChecking) | |||
@@ -269,11 +274,24 @@ namespace Shadowsocks.View | |||
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) | |||
{ | |||
_notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked; | |||
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\""; | |||
System.Diagnostics.Process.Start("explorer.exe", argument); | |||
if (updateChecker.NewVersionFound) | |||
{ | |||
updateChecker.NewVersionFound = false; /* Reset the flag */ | |||
if (System.IO.File.Exists(updateChecker.LatestVersionLocalName)) | |||
{ | |||
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\""; | |||
System.Diagnostics.Process.Start("explorer.exe", argument); | |||
} | |||
} | |||
} | |||
private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e) | |||
{ | |||
if (updateChecker.NewVersionFound) | |||
{ | |||
updateChecker.NewVersionFound = false; /* Reset the flag */ | |||
} | |||
} | |||
private void LoadCurrentConfiguration() | |||
{ | |||
@@ -324,7 +342,6 @@ namespace Shadowsocks.View | |||
{ | |||
item.Checked = true; | |||
} | |||
} | |||
} | |||
@@ -342,10 +359,36 @@ namespace Shadowsocks.View | |||
} | |||
} | |||
private void ShowLogForms() | |||
{ | |||
if (logForms.Count == 0) | |||
{ | |||
LogForm f = new LogForm(controller, Logging.LogFilePath); | |||
f.Show(); | |||
f.FormClosed += logForm_FormClosed; | |||
logForms.Add(f); | |||
logFormsVisible = true; | |||
} | |||
else | |||
{ | |||
logFormsVisible = !logFormsVisible; | |||
foreach (LogForm f in logForms) | |||
{ | |||
f.Visible = logFormsVisible; | |||
} | |||
} | |||
} | |||
void logForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
logForms.Remove((LogForm)sender); | |||
} | |||
void configForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
configForm = null; | |||
Util.Utils.ReleaseMemory(true); | |||
Utils.ReleaseMemory(true); | |||
ShowFirstTimeBalloon(); | |||
} | |||
@@ -378,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) | |||
@@ -436,11 +491,13 @@ 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(controller, argument).Show(); | |||
logForms.Add(f); | |||
} | |||
private void StatisticsConfigItem_Click(object sender, EventArgs e) | |||
{ | |||
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller); | |||
@@ -549,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")); | |||
} | |||
} | |||
@@ -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(); | |||
@@ -85,6 +83,9 @@ namespace Shadowsocks.View | |||
{ | |||
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; | |||
@@ -8,11 +8,11 @@ | |||
<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.8.0" newVersion="2.6.8.0" /> | |||
<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.8.0" newVersion="2.6.8.0" /> | |||
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" /> | |||
</dependentAssembly> | |||
</assemblyBinding> | |||
</runtime> | |||
@@ -1,9 +1,11 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<packages> | |||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net4-client" developmentDependency="true" /> | |||
<package id="Fody" version="1.29.3" targetFramework="net4-client" developmentDependency="true" /> | |||
<package id="Microsoft.Bcl" version="1.1.8" targetFramework="net4-client" /> | |||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net4-client" /> | |||
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net4-client" /> | |||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net4-client" /> | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<packages> | |||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net4-client" developmentDependency="true" /> | |||
<package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net40-client" /> | |||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net40-client" /> | |||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net40-client" /> | |||
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net4-client" /> | |||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net40-client" /> | |||
<package id="System.Net.Http" version="2.0.20710.0" targetFramework="net40-client" /> | |||
</packages> |
@@ -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> | |||
@@ -68,21 +69,18 @@ | |||
<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> | |||
<EmbedInteropTypes>False</EmbedInteropTypes> | |||
</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> | |||
<EmbedInteropTypes>False</EmbedInteropTypes> | |||
</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> | |||
<EmbedInteropTypes>False</EmbedInteropTypes> | |||
</Reference> | |||
<Reference Include="Microsoft.VisualBasic" /> | |||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Newtonsoft.Json.7.0.1\lib\net40\Newtonsoft.Json.dll</HintPath> | |||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="PresentationCore" /> | |||
@@ -90,22 +88,26 @@ | |||
<Reference Include="System" /> | |||
<Reference Include="System.Data" /> | |||
<Reference Include="System.Drawing" /> | |||
<Reference Include="System.IO, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.IO.dll</HintPath> | |||
<Reference Include="System.IO, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath> | |||
<Private>True</Private> | |||
<EmbedInteropTypes>False</EmbedInteropTypes> | |||
</Reference> | |||
<Reference Include="System.Net" /> | |||
<Reference Include="System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> | |||
<Reference Include="System.Runtime, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Runtime.dll</HintPath> | |||
<Reference Include="System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll</HintPath> | |||
<Private>True</Private> | |||
<EmbedInteropTypes>False</EmbedInteropTypes> | |||
</Reference> | |||
<Reference Include="System.Threading.Tasks, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Threading.Tasks.dll</HintPath> | |||
<Reference Include="System.Net.Http.WebRequest, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Runtime, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Threading.Tasks, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath> | |||
<Private>True</Private> | |||
<EmbedInteropTypes>False</EmbedInteropTypes> | |||
</Reference> | |||
<Reference Include="System.Windows.Forms" /> | |||
<Reference Include="System.Windows.Forms.DataVisualization" /> | |||
@@ -162,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" /> | |||
@@ -184,11 +185,11 @@ | |||
<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" /> | |||
@@ -323,18 +324,15 @@ | |||
</BootstrapperPackage> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<Import Project="3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" /> | |||
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''"> | |||
<Error Condition="!Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" /> | |||
<Error Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" /> | |||
</Target> | |||
<Import Project="3rd\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('3rd\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets')" /> | |||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | |||
<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\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Fody.1.29.3\build\portable-net+sl+win+wpa+wp\Fody.targets'))" /> | |||
<Error Condition="!Exists('3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> | |||
<Error Condition="!Exists('3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets'))" /> | |||
</Target> | |||
<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"> | |||
@@ -22,6 +22,21 @@ namespace test | |||
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]; | |||
@@ -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> |
@@ -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"> | |||