diff --git a/Netch/Models/Arguments.cs b/Netch/Models/Arguments.cs new file mode 100644 index 00000000..e2e5a6cf --- /dev/null +++ b/Netch/Models/Arguments.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Netch.Utils; + +namespace Netch.Models +{ + public static class Arguments + { + public static string Format(IEnumerable a) + { + var arguments = a.ToList(); + if (arguments.Count % 2 != 0) + throw new FormatException("missing last argument value"); + + var tokens = new List(); + + for (var i = 0; i < arguments.Count; i += 2) + { + var keyObj = arguments[i]; + var valueObj = arguments[i + 1]; + + if (keyObj is not string key) + throw new FormatException($"argument key at array index {i} is not string"); + + switch (valueObj) + { + case SpecialArgument.Flag: + tokens.Add(key); + break; + case null: + case string value when value.IsNullOrWhiteSpace(): + continue; + default: + tokens.Add(key); + tokens.Add(valueObj.ToString()!); + break; + } + } + + return string.Join(' ', tokens); + } + } + + public enum SpecialArgument + { + Flag + } +} \ No newline at end of file diff --git a/Netch/Models/ParameterBase.cs b/Netch/Models/ParameterBase.cs deleted file mode 100644 index 52d2e06f..00000000 --- a/Netch/Models/ParameterBase.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; - -namespace Netch.Models -{ - public abstract class ParameterBase - { - // null value par - - private readonly bool _full; - - protected readonly string ParametersSeparate = " "; - protected readonly string Separate = " "; - protected readonly string VerbPrefix = "-"; - protected readonly string FullPrefix = "--"; - - protected ParameterBase() - { - _full = !GetType().IsDefined(typeof(VerbAttribute)); - } - - public override string ToString() - { - var parameters = GetType().GetProperties().Select(PropToParameter).Where(s => s != null).Cast(); - return string.Join(ParametersSeparate, parameters).Trim(); - } - - private string? PropToParameter(PropertyInfo p) - { - // prefix - bool full; - if (p.IsDefined(typeof(VerbAttribute))) - full = false; - else if (p.IsDefined(typeof(FullAttribute))) - full = true; - else - full = _full; - - var prefix = full ? FullPrefix : VerbPrefix; - // key - var key = p.GetCustomAttribute()?.Name ?? p.Name; - - // build - var value = p.GetValue(this); - switch (value) - { - case bool b: - return b ? $"{prefix}{key}" : null; - default: - if (string.IsNullOrWhiteSpace(value?.ToString())) - return p.IsDefined(typeof(OptionalAttribute)) ? null : throw new RequiredArgumentValueInvalidException(p.Name, this, null); - - if (p.IsDefined(typeof(QuoteAttribute))) - value = $"\"{value}\""; - - return $"{prefix}{key}{Separate}{value}"; - } - } - } - - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] - public class VerbAttribute : Attribute - { - // Don't use verb and full both on one class or property - // if you did, will take verb - } - - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] - public class FullAttribute : Attribute - { - } - - [AttributeUsage(AttributeTargets.Property)] - public class OptionalAttribute : Attribute - { - } - - [AttributeUsage(AttributeTargets.Property)] - public class QuoteAttribute : Attribute - { - } - - [AttributeUsage(AttributeTargets.Property)] - public class RealNameAttribute : Attribute - { - public string Name { get; } - - public RealNameAttribute(string name) - { - Name = name; - } - } - - [Serializable] - public class RequiredArgumentValueInvalidException : Exception - { - public string? ArgumentName { get; } - - public object? ArgumentObject { get; } - - private readonly string? _message; - - private const string DefaultMessage = "{0}'s Argument \"{1}\" value invalid. A required argument's value can't be null or empty."; - - public override string Message => _message ?? string.Format(DefaultMessage, ArgumentObject!.GetType(), ArgumentName); - - public RequiredArgumentValueInvalidException() - { - _message = "Some Argument value invalid. A required argument value's can't be null or empty."; - } - - public RequiredArgumentValueInvalidException(string argumentName, object argumentObject, string? message) - { - ArgumentName = argumentName; - ArgumentObject = argumentObject; - _message = message; - } - } -} \ No newline at end of file diff --git a/Netch/Servers/Shadowsocks/SSController.cs b/Netch/Servers/Shadowsocks/SSController.cs index 64ddda5a..12edf926 100644 --- a/Netch/Servers/Shadowsocks/SSController.cs +++ b/Netch/Servers/Shadowsocks/SSController.cs @@ -27,48 +27,21 @@ namespace Netch.Servers.Shadowsocks { var server = (Shadowsocks)s; - var command = new SSParameter + var arguments = new object?[] { - s = await server.AutoResolveHostnameAsync(), - p = server.Port, - b = this.LocalAddress(), - l = this.Socks5LocalPort(), - m = server.EncryptMethod, - k = server.Password, - u = true, - plugin = server.Plugin, - plugin_opts = server.PluginOption + "-s", await server.AutoResolveHostnameAsync(), + "-p", server.Port, + "-b", this.LocalAddress(), + "-l", this.Socks5LocalPort(), + "-m", server.EncryptMethod, + "-k", server.Password, + "-u", SpecialArgument.Flag, + "--plugin", server.Plugin, + "--plugin-opts", server.PluginOption }; - await StartGuardAsync(command.ToString()); + await StartGuardAsync(Arguments.Format(arguments)); return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname); } - - [Verb] - private class SSParameter : ParameterBase - { - public string? s { get; set; } - - public ushort? p { get; set; } - - public string? b { get; set; } - - public ushort? l { get; set; } - - public string? m { get; set; } - - public string? k { get; set; } - - public bool u { get; set; } - - [Full] [Optional] public string? plugin { get; set; } - - [Full] - [Optional] - [RealName("plugin-opts")] - public string? plugin_opts { get; set; } - - [Full] [Quote] [Optional] public string? acl { get; set; } - } } } \ No newline at end of file diff --git a/Netch/Servers/ShadowsocksR/SSRController.cs b/Netch/Servers/ShadowsocksR/SSRController.cs index 5fb37640..4989d189 100644 --- a/Netch/Servers/ShadowsocksR/SSRController.cs +++ b/Netch/Servers/ShadowsocksR/SSRController.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; @@ -27,62 +28,24 @@ namespace Netch.Servers.ShadowsocksR { var server = (ShadowsocksR)s; - var command = new SSRParameter + var arguments = new object?[] { - s = await server.AutoResolveHostnameAsync(), - p = server.Port, - k = server.Password, - m = server.EncryptMethod, - t = "120", - O = server.Protocol, - G = server.ProtocolParam, - o = server.OBFS, - g = server.OBFSParam, - b = this.LocalAddress(), - l = this.Socks5LocalPort(), - u = true + "-s", await server.AutoResolveHostnameAsync(), + "-p", server.Port, + "-k", server.Password, + "-m", server.EncryptMethod, + "-t", 120, + "-O", server.Protocol, + "-G", server.ProtocolParam, + "-o", server.OBFS, + "-g", server.OBFSParam, + "-b", this.LocalAddress(), + "-l", this.Socks5LocalPort(), + "-u", SpecialArgument.Flag }; - await StartGuardAsync(command.ToString()); - return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(),server.Hostname); - } - - [Verb] - class SSRParameter : ParameterBase - { - public string? s { get; set; } - - public ushort? p { get; set; } - - [Quote] - public string? k { get; set; } - - public string? m { get; set; } - - public string? t { get; set; } - - [Optional] - public string? O { get; set; } - - [Optional] - public string? G { get; set; } - - [Optional] - public string? o { get; set; } - - [Optional] - public string? g { get; set; } - - public string? b { get; set; } - - public ushort? l { get; set; } - - public bool u { get; set; } - - [Full] - [Quote] - [Optional] - public string? acl { get; set; } + await StartGuardAsync(Arguments.Format(arguments)); + return new Socks5Bridge(IPAddress.Loopback.ToString(), this.Socks5LocalPort(), server.Hostname); } } } \ No newline at end of file