Browse Source

Merge pull request #2904 from studentmain/always-sip002

Always sip002
tags/4.2.0.0
Allen Zhu GitHub 4 years ago
parent
commit
d423a24c57
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 166 additions and 158 deletions
  1. +6
    -6
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  2. +2
    -2
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  3. +1
    -42
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  4. +6
    -6
      shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs
  5. +2
    -2
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
  6. +16
    -18
      shadowsocks-csharp/Model/Configuration.cs
  7. +124
    -73
      shadowsocks-csharp/Model/Server.cs
  8. +2
    -2
      shadowsocks-csharp/View/ConfigForm.cs
  9. +2
    -2
      shadowsocks-csharp/View/MenuViewController.cs
  10. +4
    -4
      shadowsocks-csharp/View/QRCodeForm.cs
  11. +1
    -1
      test/UrlTest.cs

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

@@ -238,7 +238,7 @@ namespace Shadowsocks.Controller
{
AppendRecord(server.Identifier(), record);
}
logger.Debug($"Ping {server.FriendlyName()} {e.RoundtripTime.Count} times, {(100 - record.PackageLoss * 100)}% packages loss, min {record.MinResponse} ms, max {record.MaxResponse} ms, avg {record.AverageResponse} ms");
logger.Debug($"Ping {server} {e.RoundtripTime.Count} times, {(100 - record.PackageLoss * 100)}% packages loss, min {record.MinResponse} ms, max {record.MaxResponse} ms, avg {record.AverageResponse} ms");
if (Interlocked.Decrement(ref state.counter) == 0)
{
Save();
@@ -450,7 +450,7 @@ namespace Shadowsocks.Controller
{
try
{
logger.Debug($"Ping {server.FriendlyName()}");
logger.Debug($"Ping {server}");
if (ip == null)
{
ip = Dns.GetHostAddresses(server.server)
@@ -466,7 +466,7 @@ namespace Shadowsocks.Controller
}
catch (Exception e)
{
logger.Error($"An exception occured while eveluating {server.FriendlyName()}");
logger.Error($"An exception occured while eveluating {server}");
logger.LogUsefulException(e);
FireCompleted(e, userstate);
}
@@ -478,19 +478,19 @@ namespace Shadowsocks.Controller
{
if (e.Reply.Status == IPStatus.Success)
{
logger.Debug($"Ping {server.FriendlyName()} {e.Reply.RoundtripTime} ms");
logger.Debug($"Ping {server} {e.Reply.RoundtripTime} ms");
RoundtripTime.Add((int?)e.Reply.RoundtripTime);
}
else
{
logger.Debug($"Ping {server.FriendlyName()} timeout");
logger.Debug($"Ping {server} timeout");
RoundtripTime.Add(null);
}
TestNext(e.UserState);
}
catch (Exception ex)
{
logger.Error($"An exception occured while eveluating {server.FriendlyName()}");
logger.Error($"An exception occured while eveluating {server}");
logger.LogUsefulException(ex);
FireCompleted(ex, e.UserState);
}


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

@@ -803,7 +803,7 @@ namespace Shadowsocks.Controller
AsyncSession session = timer.Session;
Server server = timer.Server;
OnFailed?.Invoke(this, new SSRelayEventArgs(_server));
Logger.Info($"{server.FriendlyName()} timed out");
Logger.Info($"{server.ToString()} timed out");
session.Remote.Close();
Close();
}
@@ -830,7 +830,7 @@ namespace Shadowsocks.Controller
_destConnected = true;
Logger.Debug($"Socket connected to ss server: {_server.FriendlyName()}");
Logger.Debug($"Socket connected to ss server: {_server.ToString()}");
TimeSpan latency = DateTime.Now - _startConnectTime;


+ 1
- 42
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -371,48 +371,7 @@ namespace Shadowsocks.Controller
public string GetServerURLForCurrentServer()
{
Server server = GetCurrentServer();
return GetServerURL(server);
}
public static string GetServerURL(Server server)
{
string tag = string.Empty;
string url = string.Empty;
if (string.IsNullOrWhiteSpace(server.plugin))
{
// For backwards compatiblity, if no plugin, use old url format
string parts = $"{server.method}:{server.password}@{server.server}:{server.server_port}";
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
url = base64;
}
else
{
// SIP002
string parts = $"{server.method}:{server.password}";
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
string websafeBase64 = base64.Replace('+', '-').Replace('/', '_').TrimEnd('=');
string pluginPart = server.plugin;
if (!string.IsNullOrWhiteSpace(server.plugin_opts))
{
pluginPart += ";" + server.plugin_opts;
}
url = string.Format(
"{0}@{1}:{2}/?plugin={3}",
websafeBase64,
server.FormatHostName(server.server),
server.server_port,
HttpUtility.UrlEncode(pluginPart, Encoding.UTF8));
}
if (!server.remarks.IsNullOrEmpty())
{
tag = $"#{HttpUtility.UrlEncode(server.remarks, Encoding.UTF8)}";
}
return $"ss://{url}{tag}";
return GetCurrentServer().GetURL(_config.generateLegacyUrl);
}
public void UpdateStatisticsConfiguration(bool enabled)


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

@@ -114,7 +114,7 @@ namespace Shadowsocks.Controller.Strategy
100 * 1000 * Math.Min(5 * 60, (now - status.lastFailure).TotalSeconds)
-2 * 5 * (Math.Min(2000, status.latency.TotalMilliseconds) / (1 + (now - status.lastTimeDetectLatency).TotalSeconds / 30 / 10) +
-0.5 * 200 * Math.Min(5, (status.lastRead - status.lastWrite).TotalSeconds));
logger.Debug(String.Format("server: {0} latency:{1} score: {2}", status.server.FriendlyName(), status.latency, status.score));
logger.Debug(String.Format("server: {0} latency:{1} score: {2}", status.server.ToString(), status.latency, status.score));
}
ServerStatus max = null;
foreach (var status in servers)
@@ -136,14 +136,14 @@ namespace Shadowsocks.Controller.Strategy
if (_currentServer == null || max.score - _currentServer.score > 200)
{
_currentServer = max;
logger.Info($"HA switching to server: {_currentServer.server.FriendlyName()}");
logger.Info($"HA switching to server: {_currentServer.server.ToString()}");
}
}
}
public void UpdateLatency(Model.Server server, TimeSpan latency)
{
logger.Debug($"latency: {server.FriendlyName()} {latency}");
logger.Debug($"latency: {server.ToString()} {latency}");
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
@@ -155,7 +155,7 @@ namespace Shadowsocks.Controller.Strategy
public void UpdateLastRead(Model.Server server)
{
logger.Debug($"last read: {server.FriendlyName()}");
logger.Debug($"last read: {server.ToString()}");
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
@@ -166,7 +166,7 @@ namespace Shadowsocks.Controller.Strategy
public void UpdateLastWrite(Model.Server server)
{
logger.Debug($"last write: {server.FriendlyName()}");
logger.Debug($"last write: {server.ToString()}");
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
@@ -177,7 +177,7 @@ namespace Shadowsocks.Controller.Strategy
public void SetFailure(Model.Server server)
{
logger.Debug($"failure: {server.FriendlyName()}");
logger.Debug($"failure: {server.ToString()}");
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))


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

@@ -109,7 +109,7 @@ namespace Shadowsocks.Controller.Strategy
var bestResult = serversWithStatistics
.Aggregate((server1, server2) => server1.score > server2.score ? server1 : server2);

LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by statistics: score {bestResult.score}");
LogWhenEnabled($"Switch to server: {bestResult.server.ToString()} by statistics: score {bestResult.score}");
_currentServer = bestResult.server;
}
catch (Exception e)
@@ -147,7 +147,7 @@ namespace Shadowsocks.Controller.Strategy

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

public void UpdateLastRead(Server server)


+ 16
- 18
shadowsocks-csharp/Model/Configuration.cs View File

@@ -11,7 +11,7 @@ namespace Shadowsocks.Model
public class Configuration
{
[JsonIgnore]
private static Logger logger = LogManager.GetCurrentClassLogger();
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public string version;
@@ -24,14 +24,10 @@ namespace Shadowsocks.Model
public bool enabled;
public bool shareOverLan;
public bool isDefault;
public bool isIPv6Enabled = false;
public int localPort;
public bool portableMode = true;
public bool showPluginOutput;
public string pacUrl;
public string geositeUrl;
public string geositeGroup = "geolocation-!cn";
public bool geositeBlacklistMode = true;
public bool useOnlinePac;
public bool secureLocalPac = true;
@@ -39,6 +35,15 @@ namespace Shadowsocks.Model
public bool autoCheckUpdate;
public bool checkPreRelease;
public bool isVerboseLogging;
// hidden options
public bool isIPv6Enabled = false; // for experimental ipv6 support
public bool generateLegacyUrl = false; // for pre-sip002 url compatibility
public string geositeUrl; // for custom geosite source (and rule group)
public string geositeGroup = "geolocation-!cn";
public bool geositeBlacklistMode = true;
//public NLogConfig.LogLevel logLevel;
public LogViewerConfig logViewer;
public ProxyConfig proxy;
@@ -48,11 +53,10 @@ namespace Shadowsocks.Model
NLogConfig nLogConfig;
private static readonly string CONFIG_FILE = "gui-config.json";
private static readonly NLogConfig.LogLevel verboseLogLevel =
#if DEBUG
NLogConfig.LogLevel.Trace;
private static readonly NLogConfig.LogLevel verboseLogLevel = NLogConfig.LogLevel.Trace;
#else
NLogConfig.LogLevel.Debug;
private static readonly NLogConfig.LogLevel verboseLogLevel = NLogConfig.LogLevel.Debug;
#endif
@@ -194,9 +198,9 @@ namespace Shadowsocks.Model
sw.Flush();
}
try
{
// apply changs to NLog.config
config.nLogConfig.SetLogLevel(config.isVerboseLogging? verboseLogLevel : NLogConfig.LogLevel.Info);
{
// apply changes to NLog.config
config.nLogConfig.SetLogLevel(config.isVerboseLogging ? verboseLogLevel : NLogConfig.LogLevel.Info);
NLogConfig.SaveXML(config.nLogConfig);
}
catch (Exception e)
@@ -212,7 +216,7 @@ namespace Shadowsocks.Model
public static Server AddDefaultServerOrServer(Configuration config, Server server = null, int? index = null)
{
if (config != null && config.configs != null)
if (config?.configs != null)
{
server = (server ?? GetDefaultServer());
@@ -231,12 +235,6 @@ namespace Shadowsocks.Model
return new Server();
}
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)


+ 124
- 73
shadowsocks-csharp/Model/Server.cs View File

@@ -5,6 +5,7 @@ using System.Text;
using System.Web;
using Shadowsocks.Controller;
using System.Text.RegularExpressions;
using System.Linq;
namespace Shadowsocks.Model
{
@@ -15,9 +16,8 @@ namespace Shadowsocks.Model
public const int DefaultPort = 8388;
#region ParseLegacyURL
public static readonly Regex
UrlFinder = new Regex(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase),
DetailsParser = new Regex(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase);
private static readonly Regex UrlFinder = new Regex(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase);
private static readonly Regex DetailsParser = new Regex(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase);
#endregion ParseLegacyURL
private const int DefaultServerTimeoutSec = 5;
@@ -44,28 +44,77 @@ namespace Shadowsocks.Model
return server == o2.server && server_port == o2.server_port;
}
public string FriendlyName()
public override string ToString()
{
if (server.IsNullOrEmpty())
{
return I18N.GetString("New server");
}
string serverStr = $"{FormatHostName(server)}:{server_port}";
string serverStr = $"{FormalHostName}:{server_port}";
return remarks.IsNullOrEmpty()
? serverStr
: $"{remarks} ({serverStr})";
}
public string FormatHostName(string hostName)
public string GetURL(bool legacyUrl = false)
{
// CheckHostName() won't do a real DNS lookup
switch (Uri.CheckHostName(hostName))
string tag = string.Empty;
string url = string.Empty;
if (legacyUrl && string.IsNullOrWhiteSpace(plugin))
{
// For backwards compatiblity, if no plugin, use old url format
string parts = $"{method}:{password}@{server}:{server_port}";
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
url = base64;
}
else
{
// SIP002
string parts = $"{method}:{password}";
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
string websafeBase64 = base64.Replace('+', '-').Replace('/', '_').TrimEnd('=');
url = string.Format(
"{0}@{1}:{2}/",
websafeBase64,
FormalHostName,
server_port
);
if (!plugin.IsNullOrWhiteSpace())
{
string pluginPart = plugin;
if (!string.IsNullOrWhiteSpace(plugin_opts))
{
pluginPart += ";" + plugin_opts;
}
string pluginQuery = "?plugin=" + HttpUtility.UrlEncode(pluginPart, Encoding.UTF8);
url += pluginQuery;
}
}
if (!remarks.IsNullOrEmpty())
{
tag = $"#{HttpUtility.UrlEncode(remarks, Encoding.UTF8)}";
}
return $"ss://{url}{tag}";
}
public string FormalHostName
{
get
{
case UriHostNameType.IPv6: // Add square bracket when IPv6 (RFC3986)
return $"[{hostName}]";
default: // IPv4 or domain name
return hostName;
// CheckHostName() won't do a real DNS lookup
switch (Uri.CheckHostName(server))
{
case UriHostNameType.IPv6: // Add square bracket when IPv6 (RFC3986)
return $"[{server}]";
default: // IPv4 or domain name
return server;
}
}
}
@@ -114,79 +163,81 @@ namespace Shadowsocks.Model
return server;
}
public static List<Server> GetServers(string ssURL)
public static Server ParseURL(string serverUrl)
{
var serverUrls = ssURL.Split('\r', '\n', ' ');
string _serverUrl = serverUrl.Trim();
if (!_serverUrl.BeginWith("ss://", StringComparison.InvariantCultureIgnoreCase))
{
return null;
}
List<Server> servers = new List<Server>();
foreach (string serverUrl in serverUrls)
Server legacyServer = ParseLegacyURL(serverUrl);
if (legacyServer != null) //legacy
{
return legacyServer;
}
else //SIP002
{
string _serverUrl = serverUrl.Trim();
if (!_serverUrl.BeginWith("ss://", StringComparison.InvariantCultureIgnoreCase))
Uri parsedUrl;
try
{
continue;
parsedUrl = new Uri(serverUrl);
}
Server legacyServer = ParseLegacyURL(serverUrl);
if (legacyServer != null) //legacy
catch (UriFormatException)
{
servers.Add(legacyServer);
return null;
}
else //SIP002
Server server = new Server
{
Uri parsedUrl;
try
{
parsedUrl = new Uri(serverUrl);
}
catch (UriFormatException)
{
continue;
}
Server server = new Server
{
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
server = parsedUrl.IdnHost,
server_port = parsedUrl.Port,
};
// parse base64 UserInfo
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
string base64 = rawUserInfo.Replace('-', '+').Replace('_', '/'); // Web-safe base64 to normal base64
string userInfo = "";
try
{
userInfo = Encoding.UTF8.GetString(Convert.FromBase64String(
base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=')));
}
catch (FormatException)
{
continue;
}
string[] userInfoParts = userInfo.Split(new char[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
continue;
}
server.method = userInfoParts[0];
server.password = userInfoParts[1];
NameValueCollection queryParameters = HttpUtility.ParseQueryString(parsedUrl.Query);
string[] pluginParts = (queryParameters["plugin"] ?? "").Split(new[] { ';' }, 2);
if (pluginParts.Length > 0)
{
server.plugin = pluginParts[0] ?? "";
}
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
server = parsedUrl.IdnHost,
server_port = parsedUrl.Port,
};
// parse base64 UserInfo
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
string base64 = rawUserInfo.Replace('-', '+').Replace('_', '/'); // Web-safe base64 to normal base64
string userInfo = "";
try
{
userInfo = Encoding.UTF8.GetString(Convert.FromBase64String(
base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=')));
}
catch (FormatException)
{
return null;
}
string[] userInfoParts = userInfo.Split(new char[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
server.method = userInfoParts[0];
server.password = userInfoParts[1];
if (pluginParts.Length > 1)
{
server.plugin_opts = pluginParts[1] ?? "";
}
NameValueCollection queryParameters = HttpUtility.ParseQueryString(parsedUrl.Query);
string[] pluginParts = (queryParameters["plugin"] ?? "").Split(new[] { ';' }, 2);
if (pluginParts.Length > 0)
{
server.plugin = pluginParts[0] ?? "";
}
servers.Add(server);
if (pluginParts.Length > 1)
{
server.plugin_opts = pluginParts[1] ?? "";
}
return server;
}
return servers;
}
public static List<Server> GetServers(string ssURL)
{
return ssURL
.Split('\r', '\n', ' ')
.Select(u => ParseURL(u))
.Where(s => s != null)
.ToList();
}
public string Identifier()


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

@@ -427,7 +427,7 @@ namespace Shadowsocks.View
ServersListBox.Items.Clear();
foreach (Server server in configuration.configs)
{
ServersListBox.Items.Add(server.FriendlyName());
ServersListBox.Items.Add(server.ToString());
}
}
@@ -502,7 +502,7 @@ namespace Shadowsocks.View
}
if (_lastSelectedIndex >= 0 && _lastSelectedIndex < _modifiedConfiguration.configs.Count)
{
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName();
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].ToString();
}
UpdateButtons();
LoadSelectedServerDetails();


+ 2
- 2
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -172,7 +172,7 @@ namespace Shadowsocks.View
}
else
{
serverInfo = config.GetCurrentServer().FriendlyName();
serverInfo = config.GetCurrentServer().ToString();
}
// show more info by hacking the P/Invoke declaration for NOTIFYICONDATA inside Windows Forms
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
@@ -478,7 +478,7 @@ namespace Shadowsocks.View
{
if (Configuration.ChecksServer(server))
{
MenuItem item = new MenuItem(server.FriendlyName());
MenuItem item = new MenuItem(server.ToString());
item.Tag = configuration.configs.FindIndex(s => s == server);
item.Click += AServerItem_Click;
items.Add(strategyCount + serverCount, item);


+ 4
- 4
shadowsocks-csharp/View/QRCodeForm.cs View File

@@ -64,14 +64,14 @@ namespace Shadowsocks.View
private void QRCodeForm_Load(object sender, EventArgs e)
{
var servers = Configuration.Load();
var serverDatas = servers.configs.Select(
Configuration config = Configuration.Load();
List<KeyValuePair<string, string>> serverDatas = config.configs.Select(
server =>
new KeyValuePair<string, string>(ShadowsocksController.GetServerURL(server), server.FriendlyName())
new KeyValuePair<string, string>(server.GetURL(config.generateLegacyUrl), server.ToString())
).ToList();
listBox1.DataSource = serverDatas;
var selectIndex = serverDatas.FindIndex(serverData => serverData.Key.StartsWith(code));
int selectIndex = serverDatas.FindIndex(serverData => serverData.Key.StartsWith(code));
if (selectIndex >= 0) listBox1.SetSelected(selectIndex, true);
}


+ 1
- 1
test/UrlTest.cs View File

@@ -243,7 +243,7 @@ namespace Shadowsocks.Test
string expected = testCase.Key;
Server config = testCase.Value;
var actual = ShadowsocksController.GetServerURL(config);
var actual = config.GetURL(true);
Assert.AreEqual(expected, actual);
}
}


Loading…
Cancel
Save