@@ -41,7 +41,7 @@ namespace Shadowsocks.WPF.Services | |||||
showPluginOutput); | showPluginOutput); | ||||
} | } | ||||
private Sip003Plugin(string plugin, string pluginOpts, List<string> pluginArgs, string serverAddress, int serverPort, bool showPluginOutput) | |||||
private Sip003Plugin(string plugin, string? pluginOpts, List<string>? pluginArgs, string serverAddress, int serverPort, bool showPluginOutput) | |||||
{ | { | ||||
if (plugin == null) throw new ArgumentNullException(nameof(plugin)); | if (plugin == null) throw new ArgumentNullException(nameof(plugin)); | ||||
if (string.IsNullOrWhiteSpace(serverAddress)) | if (string.IsNullOrWhiteSpace(serverAddress)) | ||||
@@ -50,7 +50,7 @@ namespace Shadowsocks.WPF.Services | |||||
} | } | ||||
if (serverPort <= 0 || serverPort > 65535) | if (serverPort <= 0 || serverPort > 65535) | ||||
{ | { | ||||
throw new ArgumentOutOfRangeException("serverPort"); | |||||
throw new ArgumentOutOfRangeException(nameof(serverPort)); | |||||
} | } | ||||
var pluginProcessStartInfo = new ProcessStartInfo | var pluginProcessStartInfo = new ProcessStartInfo | ||||
@@ -68,8 +68,9 @@ namespace Shadowsocks.WPF.Services | |||||
["SS_PLUGIN_OPTIONS"] = pluginOpts | ["SS_PLUGIN_OPTIONS"] = pluginOpts | ||||
} | } | ||||
}; | }; | ||||
foreach (var arg in pluginArgs) | |||||
pluginProcessStartInfo.ArgumentList.Add(arg); | |||||
if (pluginArgs != null) | |||||
foreach (var arg in pluginArgs) | |||||
pluginProcessStartInfo.ArgumentList.Add(arg); | |||||
_pluginProcess = new Process() | _pluginProcess = new Process() | ||||
{ | { | ||||
@@ -119,7 +120,7 @@ namespace Shadowsocks.WPF.Services | |||||
return true; | return true; | ||||
} | } | ||||
public string ExpandEnvironmentVariables(string name, StringDictionary? environmentVariables = null) | |||||
public static string ExpandEnvironmentVariables(string name, StringDictionary? environmentVariables = null) | |||||
{ | { | ||||
// Expand the environment variables from the new process itself | // Expand the environment variables from the new process itself | ||||
if (environmentVariables != null) | if (environmentVariables != null) | ||||
@@ -134,11 +135,11 @@ namespace Shadowsocks.WPF.Services | |||||
return name; | return name; | ||||
} | } | ||||
static int GetNextFreeTcpPort() | |||||
public static int GetNextFreeTcpPort() | |||||
{ | { | ||||
var l = new TcpListener(IPAddress.Loopback, 0); | var l = new TcpListener(IPAddress.Loopback, 0); | ||||
l.Start(); | l.Start(); | ||||
int port = ((IPEndPoint)l.LocalEndpoint).Port; | |||||
var port = ((IPEndPoint)l.LocalEndpoint).Port; | |||||
l.Stop(); | l.Stop(); | ||||
return port; | return port; | ||||
} | } | ||||
@@ -1,6 +1,5 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | |||||
using System.Text.Json.Serialization; | using System.Text.Json.Serialization; | ||||
namespace Shadowsocks.Models | namespace Shadowsocks.Models | ||||
@@ -27,12 +26,14 @@ namespace Shadowsocks.Models | |||||
/// Gets or sets the data usage in bytes. | /// Gets or sets the data usage in bytes. | ||||
/// The value is fetched from SIP008 provider. | /// The value is fetched from SIP008 provider. | ||||
/// </summary> | /// </summary> | ||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | |||||
public ulong BytesUsed { get; set; } | public ulong BytesUsed { get; set; } | ||||
/// <summary> | /// <summary> | ||||
/// Gets or sets the data remaining to be used in bytes. | /// Gets or sets the data remaining to be used in bytes. | ||||
/// The value is fetched from SIP008 provider. | /// The value is fetched from SIP008 provider. | ||||
/// </summary> | /// </summary> | ||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | |||||
public ulong BytesRemaining { get; set; } | public ulong BytesRemaining { get; set; } | ||||
public Group() : this(string.Empty) | public Group() : this(string.Empty) | ||||
@@ -31,13 +31,13 @@ namespace Shadowsocks.Models | |||||
/// Gets or sets the plugin executable filename. | /// Gets or sets the plugin executable filename. | ||||
/// </summary> | /// </summary> | ||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | ||||
public string Plugin { get; set; } | |||||
public string? Plugin { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// Gets or sets the plugin options passed as environment variables. | /// Gets or sets the plugin options passed as environment variables. | ||||
/// </summary> | /// </summary> | ||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | ||||
public string PluginOpts { get; set; } | |||||
public string? PluginOpts { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// Gets or sets the server name. | /// Gets or sets the server name. | ||||
@@ -1,6 +1,5 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | |||||
using System.Text.Json.Serialization; | using System.Text.Json.Serialization; | ||||
namespace Shadowsocks.Models | namespace Shadowsocks.Models | ||||
@@ -23,17 +22,17 @@ namespace Shadowsocks.Models | |||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | ||||
public string Plugin { get; set; } | |||||
public string? Plugin { get; set; } | |||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | ||||
public string PluginOpts { get; set; } | |||||
public string? PluginOpts { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// Gets or sets the arguments passed to the plugin process. | /// Gets or sets the arguments passed to the plugin process. | ||||
/// </summary> | /// </summary> | ||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] | ||||
public List<string> PluginArgs { get; set; } | |||||
public List<string>? PluginArgs { get; set; } | |||||
/// <inheritdoc/> | /// <inheritdoc/> | ||||
[JsonPropertyName("remarks")] | [JsonPropertyName("remarks")] | ||||
@@ -49,9 +48,6 @@ namespace Shadowsocks.Models | |||||
Port = 8388; | Port = 8388; | ||||
Password = ""; | Password = ""; | ||||
Method = "chacha20-ietf-poly1305"; | Method = "chacha20-ietf-poly1305"; | ||||
Plugin = ""; | |||||
PluginOpts = ""; | |||||
PluginArgs = new(); | |||||
Name = ""; | Name = ""; | ||||
Uuid = ""; | Uuid = ""; | ||||
} | } | ||||
@@ -66,7 +62,7 @@ namespace Shadowsocks.Models | |||||
/// <returns></returns> | /// <returns></returns> | ||||
public Uri ToUrl() | public Uri ToUrl() | ||||
{ | { | ||||
UriBuilder uriBuilder = new UriBuilder("ss", Host, Port) | |||||
UriBuilder uriBuilder = new("ss", Host, Port) | |||||
{ | { | ||||
UserName = Utilities.Base64Url.Encode($"{Method}:{Password}"), | UserName = Utilities.Base64Url.Encode($"{Method}:{Password}"), | ||||
Fragment = Name, | Fragment = Name, | ||||
@@ -99,17 +95,20 @@ namespace Shadowsocks.Models | |||||
var userinfoSplitArray = userinfo.Split(':', 2); | var userinfoSplitArray = userinfo.Split(':', 2); | ||||
var method = userinfoSplitArray[0]; | var method = userinfoSplitArray[0]; | ||||
var password = userinfoSplitArray[1]; | var password = userinfoSplitArray[1]; | ||||
var host = uri.HostNameType == UriHostNameType.IPv6 ? uri.Host[1..^1] : uri.Host; | |||||
var escapedFragment = string.IsNullOrEmpty(uri.Fragment) ? uri.Fragment : uri.Fragment[1..]; | |||||
var name = Uri.UnescapeDataString(escapedFragment); | |||||
server = new Server() | server = new Server() | ||||
{ | { | ||||
Name = uri.Fragment, | |||||
Name = name, | |||||
Uuid = new Guid().ToString(), | Uuid = new Guid().ToString(), | ||||
Host = uri.Host, | |||||
Host = host, | |||||
Port = uri.Port, | Port = uri.Port, | ||||
Password = password, | Password = password, | ||||
Method = method, | Method = method, | ||||
}; | }; | ||||
// find the plugin query | // find the plugin query | ||||
var parsedQueriesArray = uri.Query.Split("?&"); | |||||
var parsedQueriesArray = uri.Query.Split('?', '&'); | |||||
var pluginQueryContent = ""; | var pluginQueryContent = ""; | ||||
foreach (var query in parsedQueriesArray) | foreach (var query in parsedQueriesArray) | ||||
{ | { | ||||
@@ -118,9 +117,15 @@ namespace Shadowsocks.Models | |||||
pluginQueryContent = query[7..]; // remove "plugin=" | pluginQueryContent = query[7..]; // remove "plugin=" | ||||
} | } | ||||
} | } | ||||
if (string.IsNullOrEmpty(pluginQueryContent)) // no plugin | |||||
return true; | |||||
var unescapedpluginQuery = Uri.UnescapeDataString(pluginQueryContent); | var unescapedpluginQuery = Uri.UnescapeDataString(pluginQueryContent); | ||||
var parsedPluginQueryArray = unescapedpluginQuery.Split(';', 2); | var parsedPluginQueryArray = unescapedpluginQuery.Split(';', 2); | ||||
if (parsedPluginQueryArray.Length == 2) // is valid plugin query | |||||
if (parsedPluginQueryArray.Length == 1) | |||||
{ | |||||
server.Plugin = parsedPluginQueryArray[0]; | |||||
} | |||||
else if (parsedPluginQueryArray.Length == 2) // is valid plugin query | |||||
{ | { | ||||
server.Plugin = parsedPluginQueryArray[0]; | server.Plugin = parsedPluginQueryArray[0]; | ||||
server.PluginOpts = parsedPluginQueryArray[1]; | server.PluginOpts = parsedPluginQueryArray[1]; | ||||
@@ -129,7 +134,7 @@ namespace Shadowsocks.Models | |||||
} | } | ||||
catch | catch | ||||
{ | { | ||||
server = new Server(); | |||||
server = new(); | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
@@ -1,5 +1,4 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | using System.Text; | ||||
namespace Shadowsocks.Utilities | namespace Shadowsocks.Utilities | ||||
@@ -14,7 +13,7 @@ namespace Shadowsocks.Utilities | |||||
public static byte[] DecodeToBytes(string base64url) | public static byte[] DecodeToBytes(string base64url) | ||||
{ | { | ||||
string base64string = base64url.Replace('_', '/').Replace('-', '+'); | |||||
var base64string = base64url.Replace('_', '/').Replace('-', '+'); | |||||
base64string = base64string.PadRight(base64string.Length + (4 - base64string.Length % 4) % 4, '='); | base64string = base64string.PadRight(base64string.Length + (4 - base64string.Length % 4) % 4, '='); | ||||
return Convert.FromBase64String(base64string); | return Convert.FromBase64String(base64string); | ||||
} | } | ||||