File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
using Shadowsocks.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
@@ -15,8 +15,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
string temppath = Utils.GetTempPath();
|
||||
LogFile = Path.Combine(temppath, "shadowsocks.log");
|
||||
LogFile = Utils.GetTempPath("shadowsocks.log");
|
||||
FileStream fs = new FileStream(LogFile, FileMode.Append);
|
||||
StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs);
|
||||
sw.AutoFlush = true;
|
||||
@@ -32,11 +31,41 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
}
|
||||
|
||||
public static void Error(object o)
|
||||
{
|
||||
Console.WriteLine("[E] " + o);
|
||||
}
|
||||
|
||||
public static void Info(object o)
|
||||
{
|
||||
Console.WriteLine(o);
|
||||
}
|
||||
|
||||
public static void Debug(object o)
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
Console.WriteLine(o);
|
||||
Console.WriteLine("[D] " + o);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Debug(EndPoint local, EndPoint remote, int len, string header = null, string tailer = null)
|
||||
{
|
||||
#if DEBUG
|
||||
if (header == null && tailer == null)
|
||||
Debug($"{local} => {remote} (size={len})");
|
||||
else if (header == null && tailer != null)
|
||||
Debug($"{local} => {remote} (size={len}), {tailer}");
|
||||
else if (header != null && tailer == null)
|
||||
Debug($"{header}: {local} => {remote} (size={len})");
|
||||
else
|
||||
Debug($"{header}: {local} => {remote} (size={len}), {tailer}");
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Debug(Socket sock, int len, string header = null, string tailer = null)
|
||||
{
|
||||
#if DEBUG
|
||||
Debug(sock.LocalEndPoint, sock.RemoteEndPoint, len, header, tailer);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -57,11 +86,19 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
else if (se.SocketErrorCode == SocketError.NotConnected)
|
||||
{
|
||||
// close when not connected
|
||||
// The application tried to send or receive data, and the System.Net.Sockets.Socket is not connected.
|
||||
}
|
||||
else if (se.SocketErrorCode == SocketError.HostUnreachable)
|
||||
{
|
||||
// There is no network route to the specified host.
|
||||
}
|
||||
else if (se.SocketErrorCode == SocketError.TimedOut)
|
||||
{
|
||||
// The connection attempt timed out, or the connected host has failed to respond.
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
Info(e);
|
||||
}
|
||||
}
|
||||
else if (e is ObjectDisposedException)
|
||||
@@ -69,7 +106,7 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
Info(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
@@ -30,8 +34,8 @@ namespace Shadowsocks.Controller
|
||||
public Statistics FilteredStatistics { get; private set; }
|
||||
public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1);
|
||||
private int Repeat => _config.RepeatTimesNum;
|
||||
private const int RetryInterval = 2*60*1000; //retry 2 minutes after failed
|
||||
private int Interval => (int) TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds;
|
||||
private const int RetryInterval = 2 * 60 * 1000; //retry 2 minutes after failed
|
||||
private int Interval => (int)TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds;
|
||||
private Timer _timer;
|
||||
private State _state;
|
||||
private List<Server> _servers;
|
||||
@@ -42,8 +46,7 @@ namespace Shadowsocks.Controller
|
||||
//static constructor to initialize every public static fields before refereced
|
||||
static AvailabilityStatistics()
|
||||
{
|
||||
var temppath = Utils.GetTempPath();
|
||||
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName);
|
||||
AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName);
|
||||
}
|
||||
|
||||
public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig)
|
||||
@@ -107,11 +110,18 @@ namespace Shadowsocks.Controller
|
||||
Logging.LogUsefulException(e);
|
||||
return null;
|
||||
}
|
||||
dynamic obj;
|
||||
if (!SimpleJson.SimpleJson.TryDeserializeObject(jsonString, out obj)) return null;
|
||||
string country = obj["country"];
|
||||
string city = obj["city"];
|
||||
string isp = obj["isp"];
|
||||
JObject obj;
|
||||
try
|
||||
{
|
||||
obj = JObject.Parse(jsonString);
|
||||
}
|
||||
catch (JsonReaderException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string country = (string)obj["country"];
|
||||
string city = (string)obj["city"];
|
||||
string isp = (string)obj["isp"];
|
||||
if (country == null || city == null || isp == null) return null;
|
||||
return new DataList {
|
||||
new DataUnit(State.Geolocation, $"\"{country} {city}\""),
|
||||
@@ -126,8 +136,7 @@ namespace Shadowsocks.Controller
|
||||
var IP = Dns.GetHostAddresses(server.server).First(ip => (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6));
|
||||
var ping = new Ping();
|
||||
var ret = new List<DataList>();
|
||||
foreach (
|
||||
var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString(DateTimePattern)))
|
||||
foreach (var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString(DateTimePattern)))
|
||||
{
|
||||
//ICMP echo. we can also set options and special bytes
|
||||
try
|
||||
@@ -146,7 +155,7 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"An exception occured when eveluating {server.FriendlyName()}");
|
||||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}");
|
||||
Logging.LogUsefulException(e);
|
||||
}
|
||||
}
|
||||
@@ -182,11 +191,11 @@ namespace Shadowsocks.Controller
|
||||
if (!File.Exists(AvailabilityStatisticsFile))
|
||||
{
|
||||
var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray());
|
||||
lines = new[] {headerLine, dataLine};
|
||||
lines = new[] { headerLine, dataLine };
|
||||
}
|
||||
else
|
||||
{
|
||||
lines = new[] {dataLine};
|
||||
lines = new[] { dataLine };
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -249,30 +258,29 @@ namespace Shadowsocks.Controller
|
||||
Logging.Debug($"loading statistics from {path}");
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval/60/1000} minutes later");
|
||||
Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval / 60 / 1000} minutes later");
|
||||
_timer.Change(RetryInterval, Interval);
|
||||
return;
|
||||
}
|
||||
RawStatistics = (from l in File.ReadAllLines(path)
|
||||
.Skip(1)
|
||||
let strings = l.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
|
||||
let rawData = new RawStatisticsData
|
||||
{
|
||||
Timestamp = ParseExactOrUnknown(strings[0]),
|
||||
ServerName = strings[1],
|
||||
ICMPStatus = strings[2],
|
||||
RoundtripTime = int.Parse(strings[3]),
|
||||
Geolocation = 5 > strings.Length ?
|
||||
null
|
||||
: strings[4],
|
||||
ISP = 6 > strings.Length ? null : strings[5]
|
||||
}
|
||||
group rawData by rawData.ServerName into server
|
||||
select new
|
||||
{
|
||||
ServerName = server.Key,
|
||||
data = server.ToList()
|
||||
}).ToDictionary(server => server.ServerName, server=> server.data);
|
||||
RawStatistics = (from l in File.ReadAllLines(path).Skip(1)
|
||||
let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
|
||||
let rawData = new RawStatisticsData
|
||||
{
|
||||
Timestamp = ParseExactOrUnknown(strings[0]),
|
||||
ServerName = strings[1],
|
||||
ICMPStatus = strings[2],
|
||||
RoundtripTime = int.Parse(strings[3]),
|
||||
Geolocation = 5 > strings.Length ?
|
||||
null
|
||||
: strings[4],
|
||||
ISP = 6 > strings.Length ? null : strings[5]
|
||||
}
|
||||
group rawData by rawData.ServerName into server
|
||||
select new
|
||||
{
|
||||
ServerName = server.Key,
|
||||
data = server.ToList()
|
||||
}).ToDictionary(server => server.ServerName, server => server.data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -301,7 +309,7 @@ namespace Shadowsocks.Controller
|
||||
public string ICMPStatus;
|
||||
public int RoundtripTime;
|
||||
public string Geolocation;
|
||||
public string ISP ;
|
||||
public string ISP;
|
||||
}
|
||||
|
||||
public class StatisticsData
|
||||
@@ -311,6 +319,5 @@ namespace Shadowsocks.Controller
|
||||
public int MinResponse;
|
||||
public int MaxResponse;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using Shadowsocks.Properties;
|
||||
using SimpleJson;
|
||||
using Shadowsocks.Util;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
@@ -14,12 +16,6 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt";
|
||||
|
||||
private static string PAC_FILE = PACServer.PAC_FILE;
|
||||
|
||||
private static string USER_RULE_FILE = PACServer.USER_RULE_FILE;
|
||||
|
||||
private static string USER_ABP_FILE = PACServer.USER_ABP_FILE;
|
||||
|
||||
public event EventHandler<ResultEventArgs> UpdateCompleted;
|
||||
|
||||
public event ErrorEventHandler Error;
|
||||
@@ -38,12 +34,13 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllText(Utils.GetTempPath("gfwlist.txt"), e.Result, Encoding.UTF8);
|
||||
List<string> lines = ParseResult(e.Result);
|
||||
if (File.Exists(USER_RULE_FILE))
|
||||
if (File.Exists(PACServer.USER_RULE_FILE))
|
||||
{
|
||||
string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8);
|
||||
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
|
||||
string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach(string rule in rules)
|
||||
foreach (string rule in rules)
|
||||
{
|
||||
if (rule.StartsWith("!") || rule.StartsWith("["))
|
||||
continue;
|
||||
@@ -51,25 +48,25 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
}
|
||||
string abpContent;
|
||||
if (File.Exists(USER_ABP_FILE))
|
||||
if (File.Exists(PACServer.USER_ABP_FILE))
|
||||
{
|
||||
abpContent = File.ReadAllText(USER_ABP_FILE, Encoding.UTF8);
|
||||
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
abpContent = Utils.UnGzip(Resources.abp_js);
|
||||
}
|
||||
abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines));
|
||||
if (File.Exists(PAC_FILE))
|
||||
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
|
||||
if (File.Exists(PACServer.PAC_FILE))
|
||||
{
|
||||
string original = File.ReadAllText(PAC_FILE, Encoding.UTF8);
|
||||
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
|
||||
if (original == abpContent)
|
||||
{
|
||||
UpdateCompleted(this, new ResultEventArgs(false));
|
||||
return;
|
||||
}
|
||||
}
|
||||
File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8);
|
||||
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8);
|
||||
if (UpdateCompleted != null)
|
||||
{
|
||||
UpdateCompleted(this, new ResultEventArgs(true));
|
||||
@@ -92,7 +89,7 @@ namespace Shadowsocks.Controller
|
||||
http.DownloadStringAsync(new Uri(GFWLIST_URL));
|
||||
}
|
||||
|
||||
public List<string> ParseResult(string response)
|
||||
public static List<string> ParseResult(string response)
|
||||
{
|
||||
byte[] bytes = Convert.FromBase64String(response);
|
||||
string content = Encoding.ASCII.GetString(bytes);
|
||||
|
||||
@@ -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,33 +1,33 @@
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
class PACServer : Listener.Service
|
||||
{
|
||||
public static string PAC_FILE = "pac.txt";
|
||||
public static readonly string PAC_FILE = "pac.txt";
|
||||
public static readonly string USER_RULE_FILE = "user-rule.txt";
|
||||
public static readonly string USER_ABP_FILE = "abp.txt";
|
||||
|
||||
public static string USER_RULE_FILE = "user-rule.txt";
|
||||
|
||||
public static string USER_ABP_FILE = "abp.txt";
|
||||
|
||||
FileSystemWatcher watcher;
|
||||
FileSystemWatcher PACFileWatcher;
|
||||
FileSystemWatcher UserRuleFileWatcher;
|
||||
private Configuration _config;
|
||||
|
||||
public event EventHandler PACFileChanged;
|
||||
public event EventHandler UserRuleFileChanged;
|
||||
|
||||
public PACServer()
|
||||
{
|
||||
this.WatchPacFile();
|
||||
this.WatchUserRuleFile();
|
||||
}
|
||||
|
||||
public void UpdateConfiguration(Configuration config)
|
||||
@@ -48,7 +48,7 @@ namespace Shadowsocks.Controller
|
||||
bool hostMatch = false, pathMatch = false, useSocks = false;
|
||||
foreach (string line in lines)
|
||||
{
|
||||
string[] kv = line.Split(new char[]{':'}, 2);
|
||||
string[] kv = line.Split(new char[] { ':' }, 2);
|
||||
if (kv.Length == 2)
|
||||
{
|
||||
if (kv[0] == "Host")
|
||||
@@ -58,14 +58,14 @@ namespace Shadowsocks.Controller
|
||||
hostMatch = true;
|
||||
}
|
||||
}
|
||||
else if (kv[0] == "User-Agent")
|
||||
{
|
||||
// we need to drop connections when changing servers
|
||||
/* if (kv[1].IndexOf("Chrome") >= 0)
|
||||
{
|
||||
useSocks = true;
|
||||
} */
|
||||
}
|
||||
//else if (kv[0] == "User-Agent")
|
||||
//{
|
||||
// // we need to drop connections when changing servers
|
||||
// if (kv[1].IndexOf("Chrome") >= 0)
|
||||
// {
|
||||
// useSocks = true;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
else if (kv.Length == 1)
|
||||
{
|
||||
@@ -144,14 +144,14 @@ Content-Type: application/x-ns-proxy-autoconfig
|
||||
Content-Length: {0}
|
||||
Connection: Close
|
||||
|
||||
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
|
||||
byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
|
||||
", Encoding.UTF8.GetBytes(pac).Length) + pac;
|
||||
byte[] response = Encoding.UTF8.GetBytes(text);
|
||||
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket);
|
||||
Util.Utils.ReleaseMemory(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
Logging.LogUsefulException(e);
|
||||
socket.Close();
|
||||
}
|
||||
}
|
||||
@@ -169,28 +169,79 @@ Connection: Close
|
||||
|
||||
private void WatchPacFile()
|
||||
{
|
||||
if (watcher != null)
|
||||
if (PACFileWatcher != null)
|
||||
{
|
||||
watcher.Dispose();
|
||||
PACFileWatcher.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;
|
||||
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)
|
||||
{
|
||||
//try
|
||||
@@ -204,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.auth, false);
|
||||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public void Start(byte[] firstPacket, int length)
|
||||
{
|
||||
this._firstPacket = firstPacket;
|
||||
this._firstPacketLength = length;
|
||||
this.HandshakeReceive();
|
||||
this.lastActivity = DateTime.Now;
|
||||
_firstPacket = firstPacket;
|
||||
_firstPacketLength = length;
|
||||
HandshakeReceive();
|
||||
lastActivity = DateTime.Now;
|
||||
}
|
||||
|
||||
private void CheckClose()
|
||||
{
|
||||
if (connectionShutdown && remoteShutdown)
|
||||
{
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +164,6 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
lock (relay.Handlers)
|
||||
{
|
||||
Logging.Debug($"connections: {relay.Handlers.Count}");
|
||||
relay.Handlers.Remove(this);
|
||||
}
|
||||
lock (this)
|
||||
@@ -213,20 +227,19 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
// reject socks 4
|
||||
response = new byte[] { 0, 91 };
|
||||
Console.WriteLine("socks 5 protocol error");
|
||||
Logging.Error("socks 5 protocol error");
|
||||
}
|
||||
Logging.Debug($"======Send Local Port, size:" + response.Length);
|
||||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,21 +253,19 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
connection.EndSend(ar);
|
||||
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// | 1 | 1 | X'00' | 1 | Variable | 2 |
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// +-----+-----+-------+------+----------+----------+
|
||||
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|
||||
// +-----+-----+-------+------+----------+----------+
|
||||
// | 1 | 1 | X'00' | 1 | Variable | 2 |
|
||||
// +-----+-----+-------+------+----------+----------+
|
||||
// Skip first 3 bytes
|
||||
// TODO validate
|
||||
Logging.Debug($"======Receive Local Port, size:" + 3);
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, 3, 0,
|
||||
new AsyncCallback(handshakeReceive2Callback), null);
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +285,6 @@ namespace Shadowsocks.Controller
|
||||
if (command == 1)
|
||||
{
|
||||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
|
||||
Logging.Debug($"======Send Local Port, size:" + response.Length);
|
||||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null);
|
||||
}
|
||||
else if (command == 3)
|
||||
@@ -284,14 +294,14 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("failed to recv data in handshakeReceive2Callback");
|
||||
this.Close();
|
||||
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
|
||||
Close();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,7 +323,6 @@ namespace Shadowsocks.Controller
|
||||
address.CopyTo(response, 4);
|
||||
response[response.Length - 1] = (byte)(port & 0xFF);
|
||||
response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
|
||||
Logging.Debug($"======Send Local Port, size:" + response.Length);
|
||||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ReadAll), true);
|
||||
}
|
||||
|
||||
@@ -328,29 +337,27 @@ namespace Shadowsocks.Controller
|
||||
if (ar.AsyncState != null)
|
||||
{
|
||||
connection.EndSend(ar);
|
||||
Logging.Debug($"======Receive Local Port, size:" + RecvSize);
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
|
||||
new AsyncCallback(ReadAll), null);
|
||||
Logging.Debug(remote, RecvSize, "TCP Relay");
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
int bytesRead = connection.EndReceive(ar);
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
Logging.Debug($"======Receive Local Port, size:" + RecvSize);
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
|
||||
new AsyncCallback(ReadAll), null);
|
||||
Logging.Debug(remote, RecvSize, "TCP Relay");
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,15 +373,16 @@ namespace Shadowsocks.Controller
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
// inner class
|
||||
private class ServerTimer : Timer
|
||||
{
|
||||
public Server Server;
|
||||
|
||||
public ServerTimer(int p) :base(p)
|
||||
public ServerTimer(int p) : base(p)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -408,14 +416,12 @@ namespace Shadowsocks.Controller
|
||||
|
||||
connected = false;
|
||||
// Connect to the remote endpoint.
|
||||
Logging.Debug($"++++++Connect Server Port");
|
||||
remote.BeginConnect(remoteEP,
|
||||
new AsyncCallback(ConnectCallback), connectTimer);
|
||||
remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,22 +437,22 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
strategy.SetFailure(server);
|
||||
}
|
||||
Console.WriteLine(String.Format("{0} timed out", server.FriendlyName()));
|
||||
Logging.Info($"{server.FriendlyName()} timed out");
|
||||
remote.Close();
|
||||
RetryConnect();
|
||||
}
|
||||
|
||||
private void RetryConnect()
|
||||
{
|
||||
if (retryCount < 4)
|
||||
if (retryCount < maxRetry)
|
||||
{
|
||||
Logging.Debug("Connection failed, retrying");
|
||||
Logging.Debug($"Connection failed, retry ({retryCount})");
|
||||
StartConnect();
|
||||
retryCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,8 +476,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
connected = true;
|
||||
|
||||
//Console.WriteLine("Socket connected to {0}",
|
||||
// remote.RemoteEndPoint.ToString());
|
||||
Logging.Debug($"Socket connected to {remote.RemoteEndPoint}");
|
||||
|
||||
var latency = DateTime.Now - _startConnectTime;
|
||||
IStrategy strategy = controller.GetCurrentStrategy();
|
||||
@@ -508,17 +513,13 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
try
|
||||
{
|
||||
Logging.Debug($"++++++Receive Server Port, size:" + RecvSize);
|
||||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
|
||||
new AsyncCallback(PipeRemoteReceiveCallback), null);
|
||||
Logging.Debug($"======Receive Local Port, size:"+ RecvSize);
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
|
||||
new AsyncCallback(PipeConnectionReceiveCallback), null);
|
||||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null);
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,10 +533,11 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
int bytesRead = remote.EndReceive(ar);
|
||||
totalRead += bytesRead;
|
||||
tcprelay.UpdateInboundCounter(bytesRead);
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
this.lastActivity = DateTime.Now;
|
||||
lastActivity = DateTime.Now;
|
||||
int bytesToSend;
|
||||
lock (decryptionLock)
|
||||
{
|
||||
@@ -545,34 +547,33 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend);
|
||||
}
|
||||
Logging.Debug($"======Send Local Port, size:" + bytesToSend);
|
||||
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)");
|
||||
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null);
|
||||
|
||||
IStrategy strategy = controller.GetCurrentStrategy();
|
||||
if (strategy != null)
|
||||
{
|
||||
strategy.UpdateLastRead(this.server);
|
||||
strategy.UpdateLastRead(server);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Console.WriteLine("bytesRead: " + bytesRead.ToString());
|
||||
connection.Shutdown(SocketShutdown.Send);
|
||||
connectionShutdown = true;
|
||||
CheckClose();
|
||||
|
||||
if (totalRead == 0)
|
||||
{
|
||||
// closed before anything received, reports as failure
|
||||
// disable this feature
|
||||
// controller.GetCurrentStrategy().SetFailure(this.server);
|
||||
}
|
||||
//if (totalRead == 0)
|
||||
//{
|
||||
// // closed before anything received, reports as failure
|
||||
// // disable this feature
|
||||
// controller.GetCurrentStrategy().SetFailure(this.server);
|
||||
//}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,6 +587,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
int bytesRead = connection.EndReceive(ar);
|
||||
totalWrite += bytesRead;
|
||||
tcprelay.UpdateOutboundCounter(bytesRead);
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
@@ -598,13 +600,13 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend);
|
||||
}
|
||||
Logging.Debug($"++++++Send Server Port, size:" + bytesToSend);
|
||||
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)");
|
||||
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null);
|
||||
|
||||
IStrategy strategy = controller.GetCurrentStrategy();
|
||||
if (strategy != null)
|
||||
{
|
||||
strategy.UpdateLastWrite(this.server);
|
||||
strategy.UpdateLastWrite(server);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -617,7 +619,7 @@ namespace Shadowsocks.Controller
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,14 +632,12 @@ namespace Shadowsocks.Controller
|
||||
try
|
||||
{
|
||||
remote.EndSend(ar);
|
||||
Logging.Debug($"======Receive Local Port, size:" + RecvSize);
|
||||
connection.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0,
|
||||
new AsyncCallback(PipeConnectionReceiveCallback), null);
|
||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,14 +650,12 @@ namespace Shadowsocks.Controller
|
||||
try
|
||||
{
|
||||
connection.EndSend(ar);
|
||||
Logging.Debug($"++++++Receive Server Port, size:" + RecvSize);
|
||||
remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0,
|
||||
new AsyncCallback(PipeRemoteReceiveCallback), null);
|
||||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.LogUsefulException(e);
|
||||
this.Close();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Shadowsocks.Controller.Strategy;
|
||||
using Shadowsocks.Encryption;
|
||||
using Shadowsocks.Model;
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Shadowsocks.Controller.Strategy;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
@@ -14,6 +14,10 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
private ShadowsocksController _controller;
|
||||
private LRUCache<IPEndPoint, UDPHandler> _cache;
|
||||
|
||||
public long outbound = 0;
|
||||
public long inbound = 0;
|
||||
|
||||
public UDPRelay(ShadowsocksController controller)
|
||||
{
|
||||
this._controller = controller;
|
||||
@@ -70,8 +74,8 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
_remoteEndPoint = new IPEndPoint(ipAddress, server.server_port);
|
||||
_remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
||||
|
||||
}
|
||||
|
||||
public void Send(byte[] data, int length)
|
||||
{
|
||||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true);
|
||||
@@ -80,15 +84,17 @@ namespace Shadowsocks.Controller
|
||||
byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES];
|
||||
int outlen;
|
||||
encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen);
|
||||
Logging.Debug($"++++++Send Server Port, size:" + outlen);
|
||||
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
|
||||
_remote.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint);
|
||||
}
|
||||
|
||||
public void Receive()
|
||||
{
|
||||
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
Logging.Debug($"++++++Receive Server Port, size:" + _buffer.Length);
|
||||
_remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null);
|
||||
}
|
||||
|
||||
public void RecvFromCallback(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
@@ -105,7 +111,7 @@ namespace Shadowsocks.Controller
|
||||
byte[] sendBuf = new byte[outlen + 3];
|
||||
Array.Copy(dataOut, 0, sendBuf, 3, outlen);
|
||||
|
||||
Logging.Debug($"======Send Local Port, size:" + (outlen + 3));
|
||||
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay");
|
||||
_local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint);
|
||||
Receive();
|
||||
}
|
||||
@@ -121,6 +127,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
try
|
||||
@@ -142,7 +149,6 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054
|
||||
class LRUCache<K, V> where V : UDPRelay.UDPHandler
|
||||
{
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
using SimpleJson;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Util;
|
||||
@@ -26,7 +23,7 @@ namespace Shadowsocks.Controller
|
||||
public string LatestVersionLocalName;
|
||||
public event EventHandler CheckUpdateCompleted;
|
||||
|
||||
public const string Version = "2.5.8";
|
||||
public const string Version = "2.5.8.1";
|
||||
|
||||
private class CheckUpdateTimer : System.Timers.Timer
|
||||
{
|
||||
@@ -79,26 +76,28 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
string response = e.Result;
|
||||
|
||||
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
|
||||
JArray result = JArray.Parse(response);
|
||||
|
||||
List<Asset> asserts = new List<Asset>();
|
||||
foreach (JsonObject release in result)
|
||||
if (result != null)
|
||||
{
|
||||
if ((bool)release["prerelease"])
|
||||
foreach (JObject release in result)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (JsonObject asset in (JsonArray)release["assets"])
|
||||
{
|
||||
Asset ass = new Asset();
|
||||
ass.Parse(asset);
|
||||
if (ass.IsNewVersion(Version))
|
||||
if ((bool)release["prerelease"])
|
||||
{
|
||||
asserts.Add(ass);
|
||||
continue;
|
||||
}
|
||||
foreach (JObject asset in (JArray)release["assets"])
|
||||
{
|
||||
Asset ass = new Asset();
|
||||
ass.Parse(asset);
|
||||
if (ass.IsNewVersion(Version))
|
||||
{
|
||||
asserts.Add(ass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asserts.Count != 0)
|
||||
{
|
||||
SortByVersions(asserts);
|
||||
@@ -129,8 +128,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
string temppath = Utils.GetTempPath();
|
||||
LatestVersionLocalName = Path.Combine(temppath, LatestVersionName);
|
||||
LatestVersionLocalName = Utils.GetTempPath(LatestVersionName);
|
||||
WebClient http = CreateWebClient();
|
||||
http.DownloadFileCompleted += Http_DownloadFileCompleted;
|
||||
http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName);
|
||||
@@ -145,7 +143,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
if(e.Error != null)
|
||||
if (e.Error != null)
|
||||
{
|
||||
Logging.LogUsefulException(e.Error);
|
||||
return;
|
||||
@@ -195,7 +193,7 @@ namespace Shadowsocks.Controller
|
||||
return CompareVersion(version, currentVersion) > 0;
|
||||
}
|
||||
|
||||
public void Parse(JsonObject asset)
|
||||
public void Parse(JObject asset)
|
||||
{
|
||||
name = (string)asset["name"];
|
||||
browser_download_url = (string)asset["browser_download_url"];
|
||||
@@ -241,6 +239,5 @@ namespace Shadowsocks.Controller
|
||||
return Asset.CompareVersion(x.version, y.version);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
using System.IO;
|
||||
using Shadowsocks.Model;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Net.Sockets;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Shadowsocks.Controller.Strategy;
|
||||
using System.Net;
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
@@ -28,6 +33,9 @@ namespace Shadowsocks.Controller
|
||||
public AvailabilityStatistics availabilityStatistics { get; private set; }
|
||||
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }
|
||||
|
||||
public long inboundCounter = 0;
|
||||
public long outboundCounter = 0;
|
||||
|
||||
private bool stopped = false;
|
||||
|
||||
private bool _systemProxyIsDirty = false;
|
||||
@@ -60,7 +68,6 @@ namespace Shadowsocks.Controller
|
||||
StartReleasingMemory();
|
||||
}
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Reload();
|
||||
@@ -300,6 +307,16 @@ namespace Shadowsocks.Controller
|
||||
Configuration.Save(_config);
|
||||
}
|
||||
|
||||
public void UpdateInboundCounter(long n)
|
||||
{
|
||||
inboundCounter += n;
|
||||
}
|
||||
|
||||
public void UpdateOutboundCounter(long n)
|
||||
{
|
||||
outboundCounter += n;
|
||||
}
|
||||
|
||||
protected void Reload()
|
||||
{
|
||||
// some logic in configuration updated the config when saving, we need to read it again
|
||||
@@ -314,6 +331,7 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
_pacServer = new PACServer();
|
||||
_pacServer.PACFileChanged += pacServer_PACFileChanged;
|
||||
_pacServer.UserRuleFileChanged += pacServer_UserRuleFileChanged;
|
||||
}
|
||||
_pacServer.UpdateConfiguration(_config);
|
||||
if (gfwListUpdater == null)
|
||||
@@ -424,6 +442,47 @@ namespace Shadowsocks.Controller
|
||||
UpdatePACFromGFWListError(this, e);
|
||||
}
|
||||
|
||||
private void pacServer_UserRuleFileChanged(object sender, EventArgs e)
|
||||
{
|
||||
// TODO: this is a dirty hack. (from code GListUpdater.http_DownloadStringCompleted())
|
||||
if (!File.Exists(Utils.GetTempPath("gfwlist.txt")))
|
||||
{
|
||||
UpdatePACFromGFWList();
|
||||
return;
|
||||
}
|
||||
List<string> lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath("gfwlist.txt")));
|
||||
if (File.Exists(PACServer.USER_RULE_FILE))
|
||||
{
|
||||
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
|
||||
string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string rule in rules)
|
||||
{
|
||||
if (rule.StartsWith("!") || rule.StartsWith("["))
|
||||
continue;
|
||||
lines.Add(rule);
|
||||
}
|
||||
}
|
||||
string abpContent;
|
||||
if (File.Exists(PACServer.USER_ABP_FILE))
|
||||
{
|
||||
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
|
||||
}
|
||||
else
|
||||
{
|
||||
abpContent = Utils.UnGzip(Resources.abp_js);
|
||||
}
|
||||
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
|
||||
if (File.Exists(PACServer.PAC_FILE))
|
||||
{
|
||||
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
|
||||
if (original == abpContent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8);
|
||||
}
|
||||
|
||||
private void StartReleasingMemory()
|
||||
{
|
||||
_ramThread = new Thread(new ThreadStart(ReleaseMemory));
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using Shadowsocks.Controller;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using Shadowsocks.Controller;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Encryption
|
||||
{
|
||||
@@ -15,13 +14,12 @@ namespace Shadowsocks.Encryption
|
||||
|
||||
static MbedTLS()
|
||||
{
|
||||
string tempPath = Utils.GetTempPath();
|
||||
string dllPath = tempPath + "/libsscrypto.dll";
|
||||
string dllPath = Utils.GetTempPath("libsscrypto.dll");
|
||||
try
|
||||
{
|
||||
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
|
||||
}
|
||||
catch (IOException ex)
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -59,10 +57,9 @@ namespace Shadowsocks.Encryption
|
||||
public extern static void md5_starts(IntPtr ctx);
|
||||
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public extern static void md5_update(IntPtr ctx, byte[] input, uint ilen );
|
||||
public extern static void md5_update(IntPtr ctx, byte[] input, uint ilen);
|
||||
|
||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
|
||||
public extern static void md5_finish(IntPtr ctx, byte[] output);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using Shadowsocks.Controller;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using Shadowsocks.Controller;
|
||||
using Shadowsocks.Properties;
|
||||
using Shadowsocks.Util;
|
||||
|
||||
namespace Shadowsocks.Encryption
|
||||
{
|
||||
@@ -15,8 +14,7 @@ namespace Shadowsocks.Encryption
|
||||
|
||||
static Sodium()
|
||||
{
|
||||
string tempPath = Utils.GetTempPath();
|
||||
string dllPath = tempPath + "/libsscrypto.dll";
|
||||
string dllPath = Utils.GetTempPath("libsscrypto.dll");
|
||||
try
|
||||
{
|
||||
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll);
|
||||
@@ -26,7 +24,7 @@ namespace Shadowsocks.Encryption
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
Logging.LogUsefulException(e);
|
||||
}
|
||||
LoadLibrary(dllPath);
|
||||
}
|
||||
@@ -47,7 +45,6 @@ namespace Shadowsocks.Encryption
|
||||
public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen,
|
||||
byte[] input, int ioff, uint ilen,
|
||||
byte[] output);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Shadowsocks.Controller;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using SimpleJson;
|
||||
|
||||
using Shadowsocks.Controller;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Shadowsocks.Model
|
||||
{
|
||||
@@ -53,7 +52,7 @@ namespace Shadowsocks.Model
|
||||
try
|
||||
{
|
||||
string configContent = File.ReadAllText(CONFIG_FILE);
|
||||
Configuration config = SimpleJson.SimpleJson.DeserializeObject<Configuration>(configContent, new JsonSerializerStrategy());
|
||||
Configuration config = JsonConvert.DeserializeObject<Configuration>(configContent);
|
||||
config.isDefault = false;
|
||||
if (config.localPort == 0)
|
||||
{
|
||||
@@ -72,7 +71,7 @@ namespace Shadowsocks.Model
|
||||
{
|
||||
if (!(e is FileNotFoundException))
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
Logging.LogUsefulException(e);
|
||||
}
|
||||
return new Configuration
|
||||
{
|
||||
@@ -110,7 +109,7 @@ namespace Shadowsocks.Model
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create)))
|
||||
{
|
||||
string jsonString = SimpleJson.SimpleJson.SerializeObject(config);
|
||||
string jsonString = JsonConvert.SerializeObject(config, Formatting.Indented);
|
||||
sw.Write(jsonString);
|
||||
sw.Flush();
|
||||
}
|
||||
@@ -166,18 +165,5 @@ namespace Shadowsocks.Model
|
||||
throw new ArgumentException(I18N.GetString("Server IP can not be blank"));
|
||||
}
|
||||
}
|
||||
|
||||
private class JsonSerializerStrategy : SimpleJson.PocoJsonSerializerStrategy
|
||||
{
|
||||
// convert string to int
|
||||
public override object DeserializeObject(object value, Type type)
|
||||
{
|
||||
if (type == typeof(Int32) && value.GetType() == typeof(string))
|
||||
{
|
||||
return Int32.Parse(value.ToString());
|
||||
}
|
||||
return base.DeserializeObject(value, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
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,
|
||||
|
||||
+12
-11
@@ -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,17 @@
|
||||
using Shadowsocks.Controller;
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Properties;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using ZXing;
|
||||
using ZXing.Common;
|
||||
using ZXing.QrCode;
|
||||
|
||||
using Shadowsocks.Controller;
|
||||
using Shadowsocks.Model;
|
||||
using Shadowsocks.Properties;
|
||||
|
||||
namespace Shadowsocks.View
|
||||
{
|
||||
public class MenuViewController
|
||||
@@ -44,6 +45,8 @@ namespace Shadowsocks.View
|
||||
private MenuItem editOnlinePACItem;
|
||||
private MenuItem autoCheckUpdatesToggleItem;
|
||||
private ConfigForm configForm;
|
||||
private List<LogForm> logForms = new List<LogForm>();
|
||||
private bool logFormsVisible = false;
|
||||
private string _urlToOpen;
|
||||
|
||||
public MenuViewController(ShadowsocksController controller)
|
||||
@@ -67,6 +70,7 @@ namespace Shadowsocks.View
|
||||
_notifyIcon.Visible = true;
|
||||
_notifyIcon.ContextMenu = contextMenu1;
|
||||
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
|
||||
_notifyIcon.MouseClick += notifyIcon1_Click;
|
||||
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
|
||||
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
|
||||
|
||||
@@ -202,7 +206,6 @@ namespace Shadowsocks.View
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void controller_ConfigChanged(object sender, EventArgs e)
|
||||
{
|
||||
LoadCurrentConfiguration();
|
||||
@@ -289,7 +292,6 @@ namespace Shadowsocks.View
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LoadCurrentConfiguration()
|
||||
{
|
||||
Configuration config = controller.GetConfigurationCopy();
|
||||
@@ -357,6 +359,32 @@ namespace Shadowsocks.View
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowLogForms()
|
||||
{
|
||||
if (logForms.Count == 0)
|
||||
{
|
||||
LogForm f = new LogForm(controller, Logging.LogFile);
|
||||
f.Show();
|
||||
f.FormClosed += logForm_FormClosed;
|
||||
|
||||
logForms.Add(f);
|
||||
logFormsVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
logFormsVisible = !logFormsVisible;
|
||||
foreach (LogForm f in logForms)
|
||||
{
|
||||
f.Visible = logFormsVisible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
logForms.Remove((LogForm)sender);
|
||||
}
|
||||
|
||||
void configForm_FormClosed(object sender, FormClosedEventArgs e)
|
||||
{
|
||||
configForm = null;
|
||||
@@ -393,6 +421,18 @@ namespace Shadowsocks.View
|
||||
Process.Start("https://github.com/shadowsocks/shadowsocks-windows");
|
||||
}
|
||||
|
||||
private void notifyIcon1_Click(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
// TODO: show something interesting
|
||||
}
|
||||
else if (e.Button == MouseButtons.Middle)
|
||||
{
|
||||
ShowLogForms();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
@@ -451,11 +491,13 @@ namespace Shadowsocks.View
|
||||
|
||||
private void ShowLogItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
string argument = Logging.LogFile;
|
||||
LogForm f = new LogForm(controller, Logging.LogFile);
|
||||
f.Show();
|
||||
f.FormClosed += logForm_FormClosed;
|
||||
|
||||
new LogForm(controller, argument).Show();
|
||||
logForms.Add(f);
|
||||
}
|
||||
|
||||
|
||||
private void StatisticsConfigItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller);
|
||||
@@ -564,9 +606,11 @@ namespace Shadowsocks.View
|
||||
Process.Start(_urlToOpen);
|
||||
}
|
||||
|
||||
private void AutoStartupItem_Click(object sender, EventArgs e) {
|
||||
private void AutoStartupItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
AutoStartupItem.Checked = !AutoStartupItem.Checked;
|
||||
if (!AutoStartup.Set(AutoStartupItem.Checked)) {
|
||||
if (!AutoStartup.Set(AutoStartupItem.Checked))
|
||||
{
|
||||
MessageBox.Show(I18N.GetString("Failed to update registry"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<Optimize>false</Optimize>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
@@ -169,7 +170,6 @@
|
||||
<Compile Include="3rd\zxing\qrcode\encoder\MaskUtil.cs" />
|
||||
<Compile Include="3rd\zxing\qrcode\encoder\MatrixUtil.cs" />
|
||||
<Compile Include="3rd\zxing\qrcode\encoder\QRCode.cs" />
|
||||
<Compile Include="3rd\SimpleJson.cs" />
|
||||
<Compile Include="3rd\zxing\qrcode\QRCodeReader.cs" />
|
||||
<Compile Include="3rd\zxing\Result.cs" />
|
||||
<Compile Include="3rd\zxing\ResultMetadataType.cs" />
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
|
||||
<package id="System.Net.Http" version="2.0.20710.0" targetFramework="net45" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
|
||||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
|
||||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
|
||||
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
|
||||
<package id="System.Net.Http" version="4.0.0" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -17,6 +17,8 @@
|
||||
<IsCodedUITest>False</IsCodedUITest>
|
||||
<TestProjectType>UnitTest</TestProjectType>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
@@ -30,8 +32,29 @@
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
@@ -79,6 +102,13 @@
|
||||
</Choose>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
Reference in New Issue
Block a user