Browse Source

Merge branch 'master' into v5/master

pull/2937/head
Student Main 4 years ago
parent
commit
94a1d68fbb
No known key found for this signature in database GPG Key ID: AA78519C208C8742
20 changed files with 1644 additions and 1634 deletions
  1. +4
    -0
      .gitattributes
  2. +533
    -533
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  3. +165
    -165
      shadowsocks-csharp/Controller/Service/Sip003Plugin.cs
  4. +2
    -2
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  5. +1
    -42
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  6. +6
    -6
      shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs
  7. +2
    -2
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
  8. +16
    -18
      shadowsocks-csharp/Model/Configuration.cs
  9. +124
    -73
      shadowsocks-csharp/Model/Server.cs
  10. +12
    -13
      shadowsocks-csharp/Program.cs
  11. +205
    -205
      shadowsocks-csharp/Proxy/HttpProxy.cs
  12. +180
    -180
      shadowsocks-csharp/Util/ProcessManagement/Job.cs
  13. +265
    -265
      shadowsocks-csharp/Util/Sockets/WrappedSocket.cs
  14. +78
    -78
      shadowsocks-csharp/View/ConfigForm.Designer.cs
  15. +2
    -2
      shadowsocks-csharp/View/ConfigForm.cs
  16. +2
    -2
      shadowsocks-csharp/View/MenuViewController.cs
  17. +42
    -42
      shadowsocks-csharp/View/ProxyForm.Designer.cs
  18. +4
    -4
      shadowsocks-csharp/View/QRCodeForm.cs
  19. +0
    -1
      shadowsocks-windows.sln
  20. +1
    -1
      test/UrlTest.cs

+ 4
- 0
.gitattributes View File

@@ -0,0 +1,4 @@
* text=auto

# geosite database
*.dat binary

+ 533
- 533
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
File diff suppressed because it is too large
View File


+ 165
- 165
shadowsocks-csharp/Controller/Service/Sip003Plugin.cs View File

@@ -1,110 +1,110 @@
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using Shadowsocks.Model;
using Shadowsocks.Util.ProcessManagement;
namespace Shadowsocks.Controller.Service
{
// https://github.com/shadowsocks/shadowsocks-org/wiki/Plugin
public sealed class Sip003Plugin : IDisposable
{
public IPEndPoint LocalEndPoint { get; private set; }
public int ProcessId => _started ? _pluginProcess.Id : 0;
private readonly object _startProcessLock = new object();
private readonly Job _pluginJob;
private readonly Process _pluginProcess;
private bool _started;
private bool _disposed;
public static Sip003Plugin CreateIfConfigured(Server server, bool showPluginOutput)
{
if (server == null)
{
throw new ArgumentNullException(nameof(server));
}
if (string.IsNullOrWhiteSpace(server.plugin))
{
return null;
}
return new Sip003Plugin(
server.plugin,
server.plugin_opts,
server.plugin_args,
server.server,
server.server_port,
showPluginOutput);
}
private Sip003Plugin(string plugin, string pluginOpts, string pluginArgs, string serverAddress, int serverPort, bool showPluginOutput)
{
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
if (string.IsNullOrWhiteSpace(serverAddress))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(serverAddress));
}
if (serverPort <= 0 || serverPort > 65535)
{
throw new ArgumentOutOfRangeException("serverPort");
}
var appPath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath);
_pluginProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = plugin,
Arguments = pluginArgs,
UseShellExecute = false,
CreateNoWindow = !showPluginOutput,
ErrorDialog = false,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = appPath ?? Environment.CurrentDirectory,
Environment =
{
["SS_REMOTE_HOST"] = serverAddress,
["SS_REMOTE_PORT"] = serverPort.ToString(),
["SS_PLUGIN_OPTIONS"] = pluginOpts
}
}
};
_pluginJob = new Job();
}
public bool StartIfNeeded()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
lock (_startProcessLock)
{
if (_started && !_pluginProcess.HasExited)
{
return false;
}
var localPort = GetNextFreeTcpPort();
LocalEndPoint = new IPEndPoint(IPAddress.Loopback, localPort);
_pluginProcess.StartInfo.Environment["SS_LOCAL_HOST"] = LocalEndPoint.Address.ToString();
_pluginProcess.StartInfo.Environment["SS_LOCAL_PORT"] = LocalEndPoint.Port.ToString();
_pluginProcess.StartInfo.Arguments = ExpandEnvironmentVariables(_pluginProcess.StartInfo.Arguments, _pluginProcess.StartInfo.EnvironmentVariables);
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using Shadowsocks.Model;
using Shadowsocks.Util.ProcessManagement;
namespace Shadowsocks.Controller.Service
{
// https://github.com/shadowsocks/shadowsocks-org/wiki/Plugin
public sealed class Sip003Plugin : IDisposable
{
public IPEndPoint LocalEndPoint { get; private set; }
public int ProcessId => _started ? _pluginProcess.Id : 0;
private readonly object _startProcessLock = new object();
private readonly Job _pluginJob;
private readonly Process _pluginProcess;
private bool _started;
private bool _disposed;
public static Sip003Plugin CreateIfConfigured(Server server, bool showPluginOutput)
{
if (server == null)
{
throw new ArgumentNullException(nameof(server));
}
if (string.IsNullOrWhiteSpace(server.plugin))
{
return null;
}
return new Sip003Plugin(
server.plugin,
server.plugin_opts,
server.plugin_args,
server.server,
server.server_port,
showPluginOutput);
}
private Sip003Plugin(string plugin, string pluginOpts, string pluginArgs, string serverAddress, int serverPort, bool showPluginOutput)
{
if (plugin == null) throw new ArgumentNullException(nameof(plugin));
if (string.IsNullOrWhiteSpace(serverAddress))
{
throw new ArgumentException("Value cannot be null or whitespace.", nameof(serverAddress));
}
if (serverPort <= 0 || serverPort > 65535)
{
throw new ArgumentOutOfRangeException("serverPort");
}
var appPath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath);
_pluginProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = plugin,
Arguments = pluginArgs,
UseShellExecute = false,
CreateNoWindow = !showPluginOutput,
ErrorDialog = false,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = appPath ?? Environment.CurrentDirectory,
Environment =
{
["SS_REMOTE_HOST"] = serverAddress,
["SS_REMOTE_PORT"] = serverPort.ToString(),
["SS_PLUGIN_OPTIONS"] = pluginOpts
}
}
};
_pluginJob = new Job();
}
public bool StartIfNeeded()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
lock (_startProcessLock)
{
if (_started && !_pluginProcess.HasExited)
{
return false;
}
var localPort = GetNextFreeTcpPort();
LocalEndPoint = new IPEndPoint(IPAddress.Loopback, localPort);
_pluginProcess.StartInfo.Environment["SS_LOCAL_HOST"] = LocalEndPoint.Address.ToString();
_pluginProcess.StartInfo.Environment["SS_LOCAL_PORT"] = LocalEndPoint.Port.ToString();
_pluginProcess.StartInfo.Arguments = ExpandEnvironmentVariables(_pluginProcess.StartInfo.Arguments, _pluginProcess.StartInfo.EnvironmentVariables);
try try
{ {
_pluginProcess.Start(); _pluginProcess.Start();
}
catch (System.ComponentModel.Win32Exception ex)
}
catch (System.ComponentModel.Win32Exception ex)
{ {
// do not use File.Exists(...), it can not handle the scenarios when the plugin file is in system environment path. // do not use File.Exists(...), it can not handle the scenarios when the plugin file is in system environment path.
// https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values // https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values
@@ -115,65 +115,65 @@ namespace Shadowsocks.Controller.Service
throw new FileNotFoundException(I18N.GetString("Cannot find the plugin program file"), _pluginProcess.StartInfo.FileName, ex); throw new FileNotFoundException(I18N.GetString("Cannot find the plugin program file"), _pluginProcess.StartInfo.FileName, ex);
} }
throw new ApplicationException(I18N.GetString("Plugin Program"), ex); throw new ApplicationException(I18N.GetString("Plugin Program"), ex);
}
_pluginJob.AddProcess(_pluginProcess.Handle);
_started = true;
}
return true;
}
public string ExpandEnvironmentVariables(string name, StringDictionary environmentVariables = null)
{
// Expand the environment variables from the new process itself
if (environmentVariables != null)
{
foreach(string key in environmentVariables.Keys)
{
name = name.Replace($"%{key}%", environmentVariables[key], StringComparison.OrdinalIgnoreCase);
}
}
// Also expand the environment variables from current main process (system)
name = Environment.ExpandEnvironmentVariables(name);
return name;
}
static int GetNextFreeTcpPort()
{
var l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
public void Dispose()
{
if (_disposed)
{
return;
}
try
{
if (!_pluginProcess.HasExited)
{
_pluginProcess.Kill();
_pluginProcess.WaitForExit();
}
}
catch (Exception) { }
finally
{
try
{
_pluginProcess.Dispose();
_pluginJob.Dispose();
}
catch (Exception) { }
_disposed = true;
}
}
}
}
_pluginJob.AddProcess(_pluginProcess.Handle);
_started = true;
}
return true;
}
public string ExpandEnvironmentVariables(string name, StringDictionary environmentVariables = null)
{
// Expand the environment variables from the new process itself
if (environmentVariables != null)
{
foreach(string key in environmentVariables.Keys)
{
name = name.Replace($"%{key}%", environmentVariables[key], StringComparison.OrdinalIgnoreCase);
}
}
// Also expand the environment variables from current main process (system)
name = Environment.ExpandEnvironmentVariables(name);
return name;
}
static int GetNextFreeTcpPort()
{
var l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
public void Dispose()
{
if (_disposed)
{
return;
}
try
{
if (!_pluginProcess.HasExited)
{
_pluginProcess.Kill();
_pluginProcess.WaitForExit();
}
}
catch (Exception) { }
finally
{
try
{
_pluginProcess.Dispose();
_pluginJob.Dispose();
}
catch (Exception) { }
_disposed = true;
}
}
}
} }

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

@@ -846,7 +846,7 @@ namespace Shadowsocks.Controller
AsyncSession session = timer.Session; AsyncSession session = timer.Session;
Server server = timer.Server; Server server = timer.Server;
OnFailed?.Invoke(this, new SSRelayEventArgs(_server)); OnFailed?.Invoke(this, new SSRelayEventArgs(_server));
Logger.Info($"{server.FriendlyName()} timed out");
Logger.Info($"{server.ToString()} timed out");
session.Remote.Close(); session.Remote.Close();
Close(); Close();
} }
@@ -873,7 +873,7 @@ namespace Shadowsocks.Controller
_destConnected = true; _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; TimeSpan latency = DateTime.Now - _startConnectTime;


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

@@ -367,48 +367,7 @@ namespace Shadowsocks.Controller
public string GetServerURLForCurrentServer() 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) 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) 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) + -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)); -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; ServerStatus max = null;
foreach (var status in servers) foreach (var status in servers)
@@ -136,14 +136,14 @@ namespace Shadowsocks.Controller.Strategy
if (_currentServer == null || max.score - _currentServer.score > 200) if (_currentServer == null || max.score - _currentServer.score > 200)
{ {
_currentServer = max; _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) public void UpdateLatency(Model.Server server, TimeSpan latency)
{ {
logger.Debug($"latency: {server.FriendlyName()} {latency}");
logger.Debug($"latency: {server.ToString()} {latency}");
ServerStatus status; ServerStatus status;
if (_serverStatus.TryGetValue(server, out status)) if (_serverStatus.TryGetValue(server, out status))
@@ -155,7 +155,7 @@ namespace Shadowsocks.Controller.Strategy
public void UpdateLastRead(Model.Server server) public void UpdateLastRead(Model.Server server)
{ {
logger.Debug($"last read: {server.FriendlyName()}");
logger.Debug($"last read: {server.ToString()}");
ServerStatus status; ServerStatus status;
if (_serverStatus.TryGetValue(server, out status)) if (_serverStatus.TryGetValue(server, out status))
@@ -166,7 +166,7 @@ namespace Shadowsocks.Controller.Strategy
public void UpdateLastWrite(Model.Server server) public void UpdateLastWrite(Model.Server server)
{ {
logger.Debug($"last write: {server.FriendlyName()}");
logger.Debug($"last write: {server.ToString()}");
ServerStatus status; ServerStatus status;
if (_serverStatus.TryGetValue(server, out status)) if (_serverStatus.TryGetValue(server, out status))
@@ -177,7 +177,7 @@ namespace Shadowsocks.Controller.Strategy
public void SetFailure(Model.Server server) public void SetFailure(Model.Server server)
{ {
logger.Debug($"failure: {server.FriendlyName()}");
logger.Debug($"failure: {server.ToString()}");
ServerStatus status; ServerStatus status;
if (_serverStatus.TryGetValue(server, out 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 var bestResult = serversWithStatistics
.Aggregate((server1, server2) => server1.score > server2.score ? server1 : server2); .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; _currentServer = bestResult.server;
} }
catch (Exception e) catch (Exception e)
@@ -147,7 +147,7 @@ namespace Shadowsocks.Controller.Strategy


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


public void UpdateLastRead(Server server) public void UpdateLastRead(Server server)


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

@@ -11,7 +11,7 @@ namespace Shadowsocks.Model
public class Configuration public class Configuration
{ {
[JsonIgnore] [JsonIgnore]
private static Logger logger = LogManager.GetCurrentClassLogger();
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public string version; public string version;
@@ -24,14 +24,10 @@ namespace Shadowsocks.Model
public bool enabled; public bool enabled;
public bool shareOverLan; public bool shareOverLan;
public bool isDefault; public bool isDefault;
public bool isIPv6Enabled = false;
public int localPort; public int localPort;
public bool portableMode = true; public bool portableMode = true;
public bool showPluginOutput; public bool showPluginOutput;
public string pacUrl; public string pacUrl;
public string geositeUrl;
public string geositeGroup = "geolocation-!cn";
public bool geositeBlacklistMode = true;
public bool useOnlinePac; public bool useOnlinePac;
public bool secureLocalPac = true; public bool secureLocalPac = true;
@@ -39,6 +35,15 @@ namespace Shadowsocks.Model
public bool autoCheckUpdate; public bool autoCheckUpdate;
public bool checkPreRelease; public bool checkPreRelease;
public bool isVerboseLogging; 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 NLogConfig.LogLevel logLevel;
public LogViewerConfig logViewer; public LogViewerConfig logViewer;
public ProxyConfig proxy; public ProxyConfig proxy;
@@ -48,11 +53,10 @@ namespace Shadowsocks.Model
NLogConfig nLogConfig; NLogConfig nLogConfig;
private static readonly string CONFIG_FILE = "gui-config.json"; private static readonly string CONFIG_FILE = "gui-config.json";
private static readonly NLogConfig.LogLevel verboseLogLevel =
#if DEBUG #if DEBUG
NLogConfig.LogLevel.Trace;
private static readonly NLogConfig.LogLevel verboseLogLevel = NLogConfig.LogLevel.Trace;
#else #else
NLogConfig.LogLevel.Debug;
private static readonly NLogConfig.LogLevel verboseLogLevel = NLogConfig.LogLevel.Debug;
#endif #endif
@@ -194,9 +198,9 @@ namespace Shadowsocks.Model
sw.Flush(); sw.Flush();
} }
try 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); NLogConfig.SaveXML(config.nLogConfig);
} }
catch (Exception e) catch (Exception e)
@@ -212,7 +216,7 @@ namespace Shadowsocks.Model
public static Server AddDefaultServerOrServer(Configuration config, Server server = null, int? index = null) 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()); server = (server ?? GetDefaultServer());
@@ -231,12 +235,6 @@ namespace Shadowsocks.Model
return new Server(); return new Server();
} }
private static void Assert(bool condition)
{
if (!condition)
throw new Exception(I18N.GetString("assertion failure"));
}
public static void CheckPort(int port) public static void CheckPort(int port)
{ {
if (port <= 0 || port > 65535) 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 System.Web;
using Shadowsocks.Controller; using Shadowsocks.Controller;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Linq;
namespace Shadowsocks.Model namespace Shadowsocks.Model
{ {
@@ -15,9 +16,8 @@ namespace Shadowsocks.Model
public const int DefaultPort = 8388; public const int DefaultPort = 8388;
#region ParseLegacyURL #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 #endregion ParseLegacyURL
private const int DefaultServerTimeoutSec = 5; private const int DefaultServerTimeoutSec = 5;
@@ -44,28 +44,77 @@ namespace Shadowsocks.Model
return server == o2.server && server_port == o2.server_port; return server == o2.server && server_port == o2.server_port;
} }
public string FriendlyName()
public override string ToString()
{ {
if (server.IsNullOrEmpty()) if (server.IsNullOrEmpty())
{ {
return I18N.GetString("New server"); return I18N.GetString("New server");
} }
string serverStr = $"{FormatHostName(server)}:{server_port}";
string serverStr = $"{FormalHostName}:{server_port}";
return remarks.IsNullOrEmpty() return remarks.IsNullOrEmpty()
? serverStr ? serverStr
: $"{remarks} ({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; 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() public string Identifier()


+ 12
- 13
shadowsocks-csharp/Program.cs View File

@@ -1,8 +1,4 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;
using NLog; using NLog;
using Microsoft.Win32; using Microsoft.Win32;
@@ -10,18 +6,23 @@ using Shadowsocks.Controller;
using Shadowsocks.Controller.Hotkeys; using Shadowsocks.Controller.Hotkeys;
using Shadowsocks.Util; using Shadowsocks.Util;
using Shadowsocks.View; using Shadowsocks.View;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Text;
using System.Net;
using System.Linq; using System.Linq;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms;
namespace Shadowsocks namespace Shadowsocks
{ {
static class Program
internal static class Program
{ {
private static Logger logger = LogManager.GetCurrentClassLogger();
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public static ShadowsocksController MainController { get; private set; } public static ShadowsocksController MainController { get; private set; }
public static MenuViewController MenuController { get; private set; } public static MenuViewController MenuController { get; private set; }
public static string[] Args { get; private set; } public static string[] Args { get; private set; }
@@ -53,7 +54,6 @@ namespace Shadowsocks
{ {
pipeExist = false; pipeExist = false;
} }
// TODO: switch to better argv parser when it's getting complicate // TODO: switch to better argv parser when it's getting complicate
List<string> alist = Args.ToList(); List<string> alist = Args.ToList();
// check --open-url param // check --open-url param
@@ -133,7 +133,6 @@ namespace Shadowsocks
{ {
MainController.AskAddServerBySSURL(addedUrl); MainController.AskAddServerBySSURL(addedUrl);
} }
Application.Run(); Application.Run();
} }
@@ -172,7 +171,7 @@ namespace Shadowsocks
logger.Info("os wake up"); logger.Info("os wake up");
if (MainController != null) if (MainController != null)
{ {
System.Threading.Tasks.Task.Factory.StartNew(() =>
Task.Factory.StartNew(() =>
{ {
Thread.Sleep(10 * 1000); Thread.Sleep(10 * 1000);
try try


+ 205
- 205
shadowsocks-csharp/Proxy/HttpProxy.cs View File

@@ -1,212 +1,212 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using NLog; using NLog;
using Shadowsocks.Controller;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Proxy
{
public class HttpProxy : IProxy
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private class FakeAsyncResult : IAsyncResult
{
public readonly HttpState innerState;
private readonly IAsyncResult r;
public FakeAsyncResult(IAsyncResult orig, HttpState state)
{
r = orig;
innerState = state;
}
public bool IsCompleted => r.IsCompleted;
public WaitHandle AsyncWaitHandle => r.AsyncWaitHandle;
public object AsyncState => innerState.AsyncState;
public bool CompletedSynchronously => r.CompletedSynchronously;
}
private class HttpState
{
public AsyncCallback Callback { get; set; }
public object AsyncState { get; set; }
public int BytesToRead;
public Exception ex { get; set; }
}
public EndPoint LocalEndPoint => _remote.LocalEndPoint;
public EndPoint ProxyEndPoint { get; private set; }
public EndPoint DestEndPoint { get; private set; }
private readonly WrappedSocket _remote = new WrappedSocket();
public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
{
ProxyEndPoint = remoteEP;
_remote.BeginConnect(remoteEP, callback, state);
}
public void EndConnectProxy(IAsyncResult asyncResult)
{
_remote.EndConnect(asyncResult);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
}
private const string HTTP_CRLF = "\r\n";
private const string HTTP_CONNECT_TEMPLATE =
"CONNECT {0} HTTP/1.1" + HTTP_CRLF +
"Host: {0}" + HTTP_CRLF +
"Proxy-Connection: keep-alive" + HTTP_CRLF +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + HTTP_CRLF +
"{1}" + // Proxy-Authorization if any
"" + HTTP_CRLF; // End with an empty line
private const string PROXY_AUTH_TEMPLATE = "Proxy-Authorization: Basic {0}" + HTTP_CRLF;
public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state, NetworkCredential auth = null)
{
DestEndPoint = destEndPoint;
using Shadowsocks.Controller;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Proxy
{
public class HttpProxy : IProxy
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private class FakeAsyncResult : IAsyncResult
{
public readonly HttpState innerState;
private readonly IAsyncResult r;
public FakeAsyncResult(IAsyncResult orig, HttpState state)
{
r = orig;
innerState = state;
}
public bool IsCompleted => r.IsCompleted;
public WaitHandle AsyncWaitHandle => r.AsyncWaitHandle;
public object AsyncState => innerState.AsyncState;
public bool CompletedSynchronously => r.CompletedSynchronously;
}
private class HttpState
{
public AsyncCallback Callback { get; set; }
public object AsyncState { get; set; }
public int BytesToRead;
public Exception ex { get; set; }
}
public EndPoint LocalEndPoint => _remote.LocalEndPoint;
public EndPoint ProxyEndPoint { get; private set; }
public EndPoint DestEndPoint { get; private set; }
private readonly WrappedSocket _remote = new WrappedSocket();
public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
{
ProxyEndPoint = remoteEP;
_remote.BeginConnect(remoteEP, callback, state);
}
public void EndConnectProxy(IAsyncResult asyncResult)
{
_remote.EndConnect(asyncResult);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
}
private const string HTTP_CRLF = "\r\n";
private const string HTTP_CONNECT_TEMPLATE =
"CONNECT {0} HTTP/1.1" + HTTP_CRLF +
"Host: {0}" + HTTP_CRLF +
"Proxy-Connection: keep-alive" + HTTP_CRLF +
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + HTTP_CRLF +
"{1}" + // Proxy-Authorization if any
"" + HTTP_CRLF; // End with an empty line
private const string PROXY_AUTH_TEMPLATE = "Proxy-Authorization: Basic {0}" + HTTP_CRLF;
public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state, NetworkCredential auth = null)
{
DestEndPoint = destEndPoint;
String authInfo = ""; String authInfo = "";
if (auth != null) if (auth != null)
{ {
string authKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth.UserName + ":" + auth.Password)); string authKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth.UserName + ":" + auth.Password));
authInfo = string.Format(PROXY_AUTH_TEMPLATE, authKey); authInfo = string.Format(PROXY_AUTH_TEMPLATE, authKey);
} }
string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint, authInfo);
var b = Encoding.UTF8.GetBytes(request);
var st = new HttpState();
st.Callback = callback;
st.AsyncState = state;
_remote.BeginSend(b, 0, b.Length, 0, HttpRequestSendCallback, st);
}
public void EndConnectDest(IAsyncResult asyncResult)
{
var state = ((FakeAsyncResult)asyncResult).innerState;
if (state.ex != null)
{
throw state.ex;
}
}
public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
{
_remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
}
public int EndSend(IAsyncResult asyncResult)
{
return _remote.EndSend(asyncResult);
}
public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
{
_remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
}
public int EndReceive(IAsyncResult asyncResult)
{
return _remote.EndReceive(asyncResult);
}
public void Shutdown(SocketShutdown how)
{
_remote.Shutdown(how);
}
public void Close()
{
_remote.Dispose();
}
private void HttpRequestSendCallback(IAsyncResult ar)
{
var state = (HttpState) ar.AsyncState;
try
{
_remote.EndSend(ar);
// start line read
new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state));
}
catch (Exception ex)
{
state.ex = ex;
state.Callback?.Invoke(new FakeAsyncResult(ar, state));
}
}
private void OnFinish(byte[] lastBytes, int index, int length, object state)
{
var st = (FakeAsyncResult)state;
if (st.innerState.ex == null)
{
if (!_established)
{
st.innerState.ex = new Exception(I18N.GetString("Proxy request failed"));
}
// TODO: save last bytes
}
st.innerState.Callback?.Invoke(st);
}
private void OnException(Exception ex, object state)
{
var st = (FakeAsyncResult) state;
st.innerState.ex = ex;
}
private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$", RegexOptions.Compiled);
private int _respondLineCount = 0;
private bool _established = false;
private bool OnLineRead(string line, object state)
{
logger.Trace(line);
if (_respondLineCount == 0)
{
var m = HttpRespondHeaderRegex.Match(line);
if (m.Success)
{
var resultCode = m.Groups[2].Value;
if ("200" != resultCode)
{
return true;
}
_established = true;
}
}
else
{
if (line.IsNullOrEmpty())
{
return true;
}
}
_respondLineCount++;
return false;
}
}
}
string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint, authInfo);
var b = Encoding.UTF8.GetBytes(request);
var st = new HttpState();
st.Callback = callback;
st.AsyncState = state;
_remote.BeginSend(b, 0, b.Length, 0, HttpRequestSendCallback, st);
}
public void EndConnectDest(IAsyncResult asyncResult)
{
var state = ((FakeAsyncResult)asyncResult).innerState;
if (state.ex != null)
{
throw state.ex;
}
}
public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
{
_remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
}
public int EndSend(IAsyncResult asyncResult)
{
return _remote.EndSend(asyncResult);
}
public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
object state)
{
_remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
}
public int EndReceive(IAsyncResult asyncResult)
{
return _remote.EndReceive(asyncResult);
}
public void Shutdown(SocketShutdown how)
{
_remote.Shutdown(how);
}
public void Close()
{
_remote.Dispose();
}
private void HttpRequestSendCallback(IAsyncResult ar)
{
var state = (HttpState) ar.AsyncState;
try
{
_remote.EndSend(ar);
// start line read
new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state));
}
catch (Exception ex)
{
state.ex = ex;
state.Callback?.Invoke(new FakeAsyncResult(ar, state));
}
}
private void OnFinish(byte[] lastBytes, int index, int length, object state)
{
var st = (FakeAsyncResult)state;
if (st.innerState.ex == null)
{
if (!_established)
{
st.innerState.ex = new Exception(I18N.GetString("Proxy request failed"));
}
// TODO: save last bytes
}
st.innerState.Callback?.Invoke(st);
}
private void OnException(Exception ex, object state)
{
var st = (FakeAsyncResult) state;
st.innerState.ex = ex;
}
private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$", RegexOptions.Compiled);
private int _respondLineCount = 0;
private bool _established = false;
private bool OnLineRead(string line, object state)
{
logger.Trace(line);
if (_respondLineCount == 0)
{
var m = HttpRespondHeaderRegex.Match(line);
if (m.Success)
{
var resultCode = m.Groups[2].Value;
if ("200" != resultCode)
{
return true;
}
_established = true;
}
}
else
{
if (line.IsNullOrEmpty())
{
return true;
}
}
_respondLineCount++;
return false;
}
}
}

+ 180
- 180
shadowsocks-csharp/Util/ProcessManagement/Job.cs View File

@@ -1,182 +1,182 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using NLog; using NLog;
using Shadowsocks.Controller;
namespace Shadowsocks.Util.ProcessManagement
{
/*
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
public class Job : IDisposable
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private IntPtr handle = IntPtr.Zero;
public Job()
{
handle = CreateJobObject(IntPtr.Zero, null);
var extendedInfoPtr = IntPtr.Zero;
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{
LimitFlags = 0x2000
};
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation = info
};
try
{
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint) length))
throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error()));
}
finally
{
if (extendedInfoPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(extendedInfoPtr);
extendedInfoPtr = IntPtr.Zero;
}
}
}
public bool AddProcess(IntPtr processHandle)
{
var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{
logger.Error("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
}
return succ;
}
public bool AddProcess(int processId)
{
return AddProcess(Process.GetProcessById(processId).Handle);
}
#region IDisposable
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
using Shadowsocks.Controller;
namespace Shadowsocks.Util.ProcessManagement
{
/*
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
public class Job : IDisposable
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private IntPtr handle = IntPtr.Zero;
public Job()
{
handle = CreateJobObject(IntPtr.Zero, null);
var extendedInfoPtr = IntPtr.Zero;
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{
LimitFlags = 0x2000
};
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation = info
};
try
{
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint) length))
throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error()));
}
finally
{
if (extendedInfoPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(extendedInfoPtr);
extendedInfoPtr = IntPtr.Zero;
}
}
}
public bool AddProcess(IntPtr processHandle)
{
var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{
logger.Error("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
}
return succ;
}
public bool AddProcess(int processId)
{
return AddProcess(Process.GetProcessById(processId).Handle);
}
#region IDisposable
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
if (disposing) if (disposing)
{
// no managed objects to free
}
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
}
~Job()
{
Dispose(false);
}
#endregion
#region Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr CreateJobObject(IntPtr a, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion
}
#region Helper classes
[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public UInt32 LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit;
public UIntPtr Affinity;
public UInt32 PriorityClass;
public UInt32 SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UIntPtr ProcessMemoryLimit;
public UIntPtr JobMemoryLimit;
public UIntPtr PeakProcessMemoryUsed;
public UIntPtr PeakJobMemoryUsed;
}
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
#endregion
}
{
// no managed objects to free
}
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
}
~Job()
{
Dispose(false);
}
#endregion
#region Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr CreateJobObject(IntPtr a, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion
}
#region Helper classes
[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public UInt32 LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit;
public UIntPtr Affinity;
public UInt32 PriorityClass;
public UInt32 SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UIntPtr ProcessMemoryLimit;
public UIntPtr JobMemoryLimit;
public UIntPtr PeakProcessMemoryUsed;
public UIntPtr PeakJobMemoryUsed;
}
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
#endregion
}

+ 265
- 265
shadowsocks-csharp/Util/Sockets/WrappedSocket.cs View File

@@ -1,268 +1,268 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Shadowsocks.Util.Sockets
{
/*
* A wrapped socket class which support both ipv4 and ipv6 based on the
* connected remote endpoint.
*
* If the server address is host name, then it may have both ipv4 and ipv6 address
* after resolving. The main idea is we don't want to resolve and choose the address
* by ourself. Instead, Socket.ConnectAsync() do handle this thing internally by trying
* each address and returning an established socket connection.
*/
public class WrappedSocket
{
public EndPoint LocalEndPoint => _activeSocket?.LocalEndPoint;
// Only used during connection and close, so it won't cost too much.
private SpinLock _socketSyncLock = new SpinLock();
private bool _disposed;
private bool Connected => _activeSocket != null;
private Socket _activeSocket;
public void BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (Connected)
{
throw new SocketException((int) SocketError.IsConnected);
}
var arg = new SocketAsyncEventArgs();
arg.RemoteEndPoint = remoteEP;
arg.Completed += OnTcpConnectCompleted;
arg.UserToken = new TcpUserToken(callback, state);
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Shadowsocks.Util.Sockets
{
/*
* A wrapped socket class which support both ipv4 and ipv6 based on the
* connected remote endpoint.
*
* If the server address is host name, then it may have both ipv4 and ipv6 address
* after resolving. The main idea is we don't want to resolve and choose the address
* by ourself. Instead, Socket.ConnectAsync() do handle this thing internally by trying
* each address and returning an established socket connection.
*/
public class WrappedSocket
{
public EndPoint LocalEndPoint => _activeSocket?.LocalEndPoint;
// Only used during connection and close, so it won't cost too much.
private SpinLock _socketSyncLock = new SpinLock();
private bool _disposed;
private bool Connected => _activeSocket != null;
private Socket _activeSocket;
public void BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (Connected)
{
throw new SocketException((int) SocketError.IsConnected);
}
var arg = new SocketAsyncEventArgs();
arg.RemoteEndPoint = remoteEP;
arg.Completed += OnTcpConnectCompleted;
arg.UserToken = new TcpUserToken(callback, state);
if(!Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, arg)) if(!Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, arg))
{ {
OnTcpConnectCompleted(this, arg); OnTcpConnectCompleted(this, arg);
}
}
private class FakeAsyncResult : IAsyncResult
{
public bool IsCompleted { get; } = true;
public WaitHandle AsyncWaitHandle { get; } = null;
public object AsyncState { get; set; }
public bool CompletedSynchronously { get; } = true;
public Exception InternalException { get; set; } = null;
}
private class TcpUserToken
{
public AsyncCallback Callback { get; }
public object AsyncState { get; }
public TcpUserToken(AsyncCallback callback, object state)
{
Callback = callback;
AsyncState = state;
}
}
private void OnTcpConnectCompleted(object sender, SocketAsyncEventArgs args)
{
using (args)
{
args.Completed -= OnTcpConnectCompleted;
var token = (TcpUserToken) args.UserToken;
if (args.SocketError != SocketError.Success)
{
var ex = args.ConnectByNameError ?? new SocketException((int) args.SocketError);
var r = new FakeAsyncResult()
{
AsyncState = token.AsyncState,
InternalException = ex
};
token.Callback(r);
}
else
{
var lockTaken = false;
if (!_socketSyncLock.IsHeldByCurrentThread)
{
_socketSyncLock.TryEnter(ref lockTaken);
}
try
{
if (Connected)
{
args.ConnectSocket.FullClose();
}
else
{
_activeSocket = args.ConnectSocket;
if (_disposed)
{
_activeSocket.FullClose();
}
var r = new FakeAsyncResult()
{
AsyncState = token.AsyncState
};
token.Callback(r);
}
}
finally
{
if (lockTaken)
{
_socketSyncLock.Exit();
}
}
}
}
}
public void EndConnect(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
var r = asyncResult as FakeAsyncResult;
if (r == null)
{
throw new ArgumentException("Invalid asyncResult.", nameof(asyncResult));
}
if (r.InternalException != null)
{
throw r.InternalException;
}
}
public void Dispose()
{
if (_disposed)
{
return;
}
var lockTaken = false;
if (!_socketSyncLock.IsHeldByCurrentThread)
{
_socketSyncLock.TryEnter(ref lockTaken);
}
try
{
_disposed = true;
_activeSocket?.FullClose();
}
finally
{
if (lockTaken)
{
_socketSyncLock.Exit();
}
}
}
public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags,
AsyncCallback callback,
object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.BeginSend(buffer, offset, size, socketFlags, callback, state);
}
public int EndSend(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.EndSend(asyncResult);
}
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags,
AsyncCallback callback,
object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.BeginReceive(buffer, offset, size, socketFlags, callback, state);
}
public int EndReceive(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.EndReceive(asyncResult);
}
public void Shutdown(SocketShutdown how)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
return;
}
_activeSocket.Shutdown(how);
}
public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue)
{
SetSocketOption(optionLevel, optionName, optionValue ? 1 : 0);
}
public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int)SocketError.NotConnected);
}
_activeSocket.SetSocketOption(optionLevel, optionName, optionValue);
}
}
}
}
}
private class FakeAsyncResult : IAsyncResult
{
public bool IsCompleted { get; } = true;
public WaitHandle AsyncWaitHandle { get; } = null;
public object AsyncState { get; set; }
public bool CompletedSynchronously { get; } = true;
public Exception InternalException { get; set; } = null;
}
private class TcpUserToken
{
public AsyncCallback Callback { get; }
public object AsyncState { get; }
public TcpUserToken(AsyncCallback callback, object state)
{
Callback = callback;
AsyncState = state;
}
}
private void OnTcpConnectCompleted(object sender, SocketAsyncEventArgs args)
{
using (args)
{
args.Completed -= OnTcpConnectCompleted;
var token = (TcpUserToken) args.UserToken;
if (args.SocketError != SocketError.Success)
{
var ex = args.ConnectByNameError ?? new SocketException((int) args.SocketError);
var r = new FakeAsyncResult()
{
AsyncState = token.AsyncState,
InternalException = ex
};
token.Callback(r);
}
else
{
var lockTaken = false;
if (!_socketSyncLock.IsHeldByCurrentThread)
{
_socketSyncLock.TryEnter(ref lockTaken);
}
try
{
if (Connected)
{
args.ConnectSocket.FullClose();
}
else
{
_activeSocket = args.ConnectSocket;
if (_disposed)
{
_activeSocket.FullClose();
}
var r = new FakeAsyncResult()
{
AsyncState = token.AsyncState
};
token.Callback(r);
}
}
finally
{
if (lockTaken)
{
_socketSyncLock.Exit();
}
}
}
}
}
public void EndConnect(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
var r = asyncResult as FakeAsyncResult;
if (r == null)
{
throw new ArgumentException("Invalid asyncResult.", nameof(asyncResult));
}
if (r.InternalException != null)
{
throw r.InternalException;
}
}
public void Dispose()
{
if (_disposed)
{
return;
}
var lockTaken = false;
if (!_socketSyncLock.IsHeldByCurrentThread)
{
_socketSyncLock.TryEnter(ref lockTaken);
}
try
{
_disposed = true;
_activeSocket?.FullClose();
}
finally
{
if (lockTaken)
{
_socketSyncLock.Exit();
}
}
}
public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags,
AsyncCallback callback,
object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.BeginSend(buffer, offset, size, socketFlags, callback, state);
}
public int EndSend(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.EndSend(asyncResult);
}
public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags,
AsyncCallback callback,
object state)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.BeginReceive(buffer, offset, size, socketFlags, callback, state);
}
public int EndReceive(IAsyncResult asyncResult)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int) SocketError.NotConnected);
}
return _activeSocket.EndReceive(asyncResult);
}
public void Shutdown(SocketShutdown how)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
return;
}
_activeSocket.Shutdown(how);
}
public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue)
{
SetSocketOption(optionLevel, optionName, optionValue ? 1 : 0);
}
public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
if (!Connected)
{
throw new SocketException((int)SocketError.NotConnected);
}
_activeSocket.SetSocketOption(optionLevel, optionName, optionValue);
}
}
}

+ 78
- 78
shadowsocks-csharp/View/ConfigForm.Designer.cs View File

@@ -1,33 +1,33 @@
namespace Shadowsocks.View
{
partial class ConfigForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
namespace Shadowsocks.View
{
partial class ConfigForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.PluginOptionsLabel = new System.Windows.Forms.Label(); this.PluginOptionsLabel = new System.Windows.Forms.Label();
@@ -672,51 +672,51 @@
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout(); this.PerformLayout();
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label IPLabel;
private System.Windows.Forms.Label ServerPortLabel;
private System.Windows.Forms.Label PasswordLabel;
private System.Windows.Forms.TextBox IPTextBox;
private System.Windows.Forms.TextBox ServerPortTextBox;
private System.Windows.Forms.Label EncryptionLabel;
private System.Windows.Forms.ComboBox EncryptionSelect;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Button OKButton;
private System.Windows.Forms.Button MyCancelButton;
private System.Windows.Forms.Button ApplyButton;
private System.Windows.Forms.Button DeleteButton;
private System.Windows.Forms.Button AddButton;
private System.Windows.Forms.GroupBox ServerGroupBox;
private System.Windows.Forms.ListBox ServersListBox;
private System.Windows.Forms.TextBox RemarksTextBox;
private System.Windows.Forms.Label RemarksLabel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5;
private System.Windows.Forms.TextBox ProxyPortTextBox;
private System.Windows.Forms.Label ProxyPortLabel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6;
private System.Windows.Forms.Button MoveDownButton;
private System.Windows.Forms.Button MoveUpButton;
private System.Windows.Forms.Button DuplicateButton;
private System.Windows.Forms.Label TimeoutLabel;
private System.Windows.Forms.TextBox TimeoutTextBox;
private System.Windows.Forms.Label PluginOptionsLabel;
private System.Windows.Forms.TextBox PluginTextBox;
private System.Windows.Forms.Label PluginLabel;
private System.Windows.Forms.TextBox PluginOptionsTextBox;
private System.Windows.Forms.CheckBox ShowPasswdCheckBox;
private System.Windows.Forms.TextBox PasswordTextBox;
private System.Windows.Forms.TextBox PluginArgumentsTextBox;
private System.Windows.Forms.Label PluginArgumentsLabel;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.CheckBox PortableModeCheckBox;
private System.Windows.Forms.CheckBox NeedPluginArgCheckBox;
}
}
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label IPLabel;
private System.Windows.Forms.Label ServerPortLabel;
private System.Windows.Forms.Label PasswordLabel;
private System.Windows.Forms.TextBox IPTextBox;
private System.Windows.Forms.TextBox ServerPortTextBox;
private System.Windows.Forms.Label EncryptionLabel;
private System.Windows.Forms.ComboBox EncryptionSelect;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Button OKButton;
private System.Windows.Forms.Button MyCancelButton;
private System.Windows.Forms.Button ApplyButton;
private System.Windows.Forms.Button DeleteButton;
private System.Windows.Forms.Button AddButton;
private System.Windows.Forms.GroupBox ServerGroupBox;
private System.Windows.Forms.ListBox ServersListBox;
private System.Windows.Forms.TextBox RemarksTextBox;
private System.Windows.Forms.Label RemarksLabel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5;
private System.Windows.Forms.TextBox ProxyPortTextBox;
private System.Windows.Forms.Label ProxyPortLabel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6;
private System.Windows.Forms.Button MoveDownButton;
private System.Windows.Forms.Button MoveUpButton;
private System.Windows.Forms.Button DuplicateButton;
private System.Windows.Forms.Label TimeoutLabel;
private System.Windows.Forms.TextBox TimeoutTextBox;
private System.Windows.Forms.Label PluginOptionsLabel;
private System.Windows.Forms.TextBox PluginTextBox;
private System.Windows.Forms.Label PluginLabel;
private System.Windows.Forms.TextBox PluginOptionsTextBox;
private System.Windows.Forms.CheckBox ShowPasswdCheckBox;
private System.Windows.Forms.TextBox PasswordTextBox;
private System.Windows.Forms.TextBox PluginArgumentsTextBox;
private System.Windows.Forms.Label PluginArgumentsLabel;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.CheckBox PortableModeCheckBox;
private System.Windows.Forms.CheckBox NeedPluginArgCheckBox;
}
}

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

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


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

@@ -174,7 +174,7 @@ namespace Shadowsocks.View
} }
else else
{ {
serverInfo = config.GetCurrentServer().FriendlyName();
serverInfo = config.GetCurrentServer().ToString();
} }
// show more info by hacking the P/Invoke declaration for NOTIFYICONDATA inside Windows Forms // show more info by hacking the P/Invoke declaration for NOTIFYICONDATA inside Windows Forms
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" + string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
@@ -485,7 +485,7 @@ namespace Shadowsocks.View
{ {
if (Configuration.ChecksServer(server)) if (Configuration.ChecksServer(server))
{ {
var name = server.FriendlyName();
var name = server.ToString();
if (!items.OfType<ToolStripMenuItem>().Any(ts => ts.Text == name)) if (!items.OfType<ToolStripMenuItem>().Any(ts => ts.Text == name))
{ {
ToolStripMenuItem item = new ToolStripMenuItem(name); ToolStripMenuItem item = new ToolStripMenuItem(name);


+ 42
- 42
shadowsocks-csharp/View/ProxyForm.Designer.cs View File

@@ -1,33 +1,33 @@
namespace Shadowsocks.View
{
partial class ProxyForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
namespace Shadowsocks.View
{
partial class ProxyForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.MyCancelButton = new System.Windows.Forms.Button(); this.MyCancelButton = new System.Windows.Forms.Button();
this.OKButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button();
this.UseProxyCheckBox = new System.Windows.Forms.CheckBox(); this.UseProxyCheckBox = new System.Windows.Forms.CheckBox();
@@ -307,17 +307,17 @@
this.flowLayoutPanel1.ResumeLayout(false); this.flowLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.CheckBox UseProxyCheckBox;
private System.Windows.Forms.Label ProxyAddrLabel;
private System.Windows.Forms.TextBox ProxyServerTextBox;
private System.Windows.Forms.Label ProxyPortLabel;
private System.Windows.Forms.TextBox ProxyPortTextBox;
private System.Windows.Forms.Button MyCancelButton;
private System.Windows.Forms.Button OKButton;
private System.Windows.Forms.Label ProxyTypeLabel;
}
#endregion
private System.Windows.Forms.CheckBox UseProxyCheckBox;
private System.Windows.Forms.Label ProxyAddrLabel;
private System.Windows.Forms.TextBox ProxyServerTextBox;
private System.Windows.Forms.Label ProxyPortLabel;
private System.Windows.Forms.TextBox ProxyPortTextBox;
private System.Windows.Forms.Button MyCancelButton;
private System.Windows.Forms.Button OKButton;
private System.Windows.Forms.Label ProxyTypeLabel;
private System.Windows.Forms.ComboBox ProxyTypeComboBox; private System.Windows.Forms.ComboBox ProxyTypeComboBox;
private System.Windows.Forms.TextBox ProxyTimeoutTextBox; private System.Windows.Forms.TextBox ProxyTimeoutTextBox;
private System.Windows.Forms.Label ProxyTimeoutLabel; private System.Windows.Forms.Label ProxyTimeoutLabel;
@@ -329,5 +329,5 @@
private System.Windows.Forms.CheckBox UseAuthCheckBox; private System.Windows.Forms.CheckBox UseAuthCheckBox;
private System.Windows.Forms.TextBox AuthUserTextBox; private System.Windows.Forms.TextBox AuthUserTextBox;
private System.Windows.Forms.TextBox AuthPwdTextBox; private System.Windows.Forms.TextBox AuthPwdTextBox;
}
}
} }

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

@@ -64,14 +64,14 @@ namespace Shadowsocks.View
private void QRCodeForm_Load(object sender, EventArgs e) 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 => server =>
new KeyValuePair<string, string>(ShadowsocksController.GetServerURL(server), server.FriendlyName())
new KeyValuePair<string, string>(server.GetURL(config.generateLegacyUrl), server.ToString())
).ToList(); ).ToList();
listBox1.DataSource = serverDatas; 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); if (selectIndex >= 0) listBox1.SetSelected(selectIndex, true);
} }


+ 0
- 1
shadowsocks-windows.sln View File

@@ -19,7 +19,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
CHANGES = CHANGES CHANGES = CHANGES
CONTRIBUTING.md = CONTRIBUTING.md CONTRIBUTING.md = CONTRIBUTING.md
LICENSE.txt = LICENSE.txt LICENSE.txt = LICENSE.txt
OPENSSL-GUIDE = OPENSSL-GUIDE
README.md = README.md README.md = README.md
EndProjectSection EndProjectSection
EndProject EndProject


+ 1
- 1
test/UrlTest.cs View File

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


Loading…
Cancel
Save